72d5607b62e2951daaef63b9f33e7f843e2fcec8
[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 = ((S64)a1*(S64)b1+(S64)a2*(S64)b2)>>16;
29   SFIXED result = (SFIXED)(a);
30   if(a!=result) 
31       fprintf(stderr, "Warning: overflow in matrix multiplication");
32   return result;
33 }
34 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
35 { S64 z = zaehler<<16;
36   S64 a = z/(S64)nenner;
37   return (SFIXED)a;
38 }
39 #undef S64
40
41 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
42 {        
43   if (!d) return NULL;
44   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
45   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
46   
47   d->tx = s1->tx + RFXSWF_SP(s1->sx,s1->r1,s2->tx,s2->ty);
48   d->ty = s1->ty + RFXSWF_SP(s1->r0,s1->sy,s2->tx,s2->ty);
49   
50   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
51   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
52
53   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
54   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
55
56   //DumpMatrix(NULL,d);
57   
58   return d;
59 }
60
61 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
62                                int x1,int y1,int x2,int y2)
63 { int dx1 = x1 - x0;
64   int dy1 = y1 - y0;
65   int dx2 = x2 - x0;
66   int dy2 = y2 - y0;
67   
68   if (!m) return NULL;
69   if ((!dx)||(!dy)) return NULL; // check DIV by zero
70
71   m->tx = x0;
72   m->ty = y0;
73   m->sx = RFXSWF_QFIX(dx1,dx);
74   m->sy = RFXSWF_QFIX(dy2,dy);
75   m->r0 = RFXSWF_QFIX(dy1,dx);
76   m->r1 = RFXSWF_QFIX(dx2,dy);
77   
78   return m;
79 }
80
81 void swf_SetDefineID(TAG * tag, U16 newid)
82 {
83   int oldlen = tag->len;
84   tag->len = 0;
85   swf_SetU16(tag, newid); /* set defining ID */
86   tag->len = oldlen;
87 }
88
89 U16 swf_GetDefineID(TAG * t)
90 // up to SWF 4.0
91 { U32 oldTagPos;
92   U16 id = 0;
93
94   oldTagPos = swf_GetTagPos(t);
95   swf_SetTagPos(t,0);
96
97   switch (swf_GetTagID(t))
98   { case ST_DEFINESHAPE:
99     case ST_DEFINESHAPE2:
100     case ST_DEFINESHAPE3:
101     case ST_DEFINEMORPHSHAPE:
102     case ST_DEFINEEDITTEXT:
103     case ST_DEFINEBITS:
104     case ST_DEFINEBITSJPEG2:
105     case ST_DEFINEBITSJPEG3:
106     case ST_DEFINEBITSLOSSLESS:
107     case ST_DEFINEBITSLOSSLESS2:
108     case ST_DEFINEBUTTON:
109     case ST_DEFINEBUTTON2:
110     case ST_DEFINEBUTTONCXFORM: //pseudodefine
111     case ST_DEFINEBUTTONSOUND: //pseudodefine
112     case ST_DEFINEFONT:
113     case ST_DEFINEFONT2:
114     case ST_DEFINEFONTINFO: //pseudodefine
115     case ST_DEFINEFONTINFO2: //pseudodefine
116     case ST_DEFINETEXT:
117     case ST_DEFINETEXT2:
118     case ST_DEFINESOUND:
119     case ST_DEFINESPRITE:
120     case ST_DEFINEMOVIE:
121     case ST_DEFINEVIDEOSTREAM:
122     case ST_GLYPHNAMES: //pseudodefine
123     case ST_VIDEOFRAME: //pseudodefine
124     case ST_NAMECHARACTER: //pseudodefine
125     case ST_DOINITACTION: //pseudodefine
126       id = swf_GetU16(t);
127       break;
128     default:
129       fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
130   }
131
132   swf_SetTagPos(t,oldTagPos);
133
134   return id;
135 }
136
137 SRECT swf_GetDefineBBox(TAG * t)
138 {
139   U32 oldTagPos;
140   U16 id = 0;
141   SRECT b1,b2;
142   memset(&b1, 0, sizeof(b1));
143
144   oldTagPos = swf_GetTagPos(t);
145   swf_SetTagPos(t,0);
146
147   swf_GetRect(0, &b1);
148
149   switch (swf_GetTagID(t))
150   { case ST_DEFINESHAPE:
151     case ST_DEFINESHAPE2:
152     case ST_DEFINESHAPE3:
153     case ST_DEFINEEDITTEXT:
154     case ST_DEFINETEXT:
155     case ST_DEFINETEXT2:
156     case ST_DEFINEVIDEOSTREAM:
157       id = swf_GetU16(t);
158       swf_GetRect(t, &b1);
159       break;
160     case ST_DEFINEMORPHSHAPE:
161       id = swf_GetU16(t);
162       swf_GetRect(t, &b1);
163       swf_GetRect(t, &b2);
164       swf_ExpandRect2(&b1, &b2);
165       break;
166     case ST_DEFINEBITSLOSSLESS:
167     case ST_DEFINEBITSLOSSLESS2:
168     case ST_DEFINEBITS:
169     case ST_DEFINEBITSJPEG2:
170     case ST_DEFINEBITSJPEG3:
171       // FIXME
172       break;
173   }
174
175   swf_SetTagPos(t,oldTagPos);
176
177   return b1;
178 }
179
180 U16 swf_GetPlaceID(TAG * t)
181 // up to SWF 4.0
182 { U32 oldTagPos;
183   U16 id = 0;
184
185   oldTagPos = swf_GetTagPos(t);
186   swf_SetTagPos(t,0);
187
188   switch (swf_GetTagID(t))
189   { case ST_PLACEOBJECT:
190     case ST_REMOVEOBJECT:
191     case ST_FREECHARACTER:
192     case ST_STARTSOUND:
193       id = swf_GetU16(t);
194       break;
195
196     case ST_PLACEOBJECT2:
197     { U8 flags = swf_GetU8(t);
198       U16 d = swf_GetU16(t);
199       id = (flags&PF_CHAR)?swf_GetU16(t):id;
200     } break;
201
202   }
203
204   swf_SetTagPos(t,oldTagPos);
205
206   return id;
207 }
208
209 static int swf_definingtagids[] =
210 {ST_DEFINESHAPE,
211  ST_DEFINESHAPE2,
212  ST_DEFINESHAPE3,
213  ST_DEFINEMORPHSHAPE,
214  ST_DEFINEFONT,
215  ST_DEFINEFONT2,
216  ST_DEFINETEXT,
217  ST_DEFINETEXT2,
218  ST_DEFINEEDITTEXT,
219  ST_DEFINEBITS,
220  ST_DEFINEBITSJPEG2,
221  ST_DEFINEBITSJPEG3,
222  ST_DEFINEBITSLOSSLESS,
223  ST_DEFINEBITSLOSSLESS2,
224  ST_DEFINEMOVIE,
225  ST_DEFINESPRITE,
226  ST_DEFINEBUTTON,
227  ST_DEFINEBUTTON2,
228  ST_DEFINESOUND,
229  ST_DEFINEVIDEOSTREAM,
230  -1
231 };
232
233 // tags which may be used inside a sprite definition
234 static int swf_spritetagids[] =
235 {ST_SHOWFRAME,
236  ST_PLACEOBJECT,
237  ST_PLACEOBJECT2,
238  ST_REMOVEOBJECT,
239  ST_REMOVEOBJECT2, //?
240  ST_DOACTION,
241  ST_STARTSOUND,
242  ST_FRAMELABEL,
243  ST_SOUNDSTREAMHEAD,
244  ST_SOUNDSTREAMHEAD2,
245  ST_SOUNDSTREAMBLOCK,
246  ST_END,
247  -1
248 };
249
250 static int swf_pseudodefiningtagids[] = 
251 {
252  ST_DEFINEFONTINFO,
253  ST_DEFINEFONTINFO2,
254  ST_DEFINEBUTTONCXFORM,
255  ST_DEFINEBUTTONSOUND,
256  ST_NAMECHARACTER,
257  ST_DOINITACTION,
258  ST_VIDEOFRAME,
259  ST_GLYPHNAMES,
260  -1
261 };
262
263 U8 swf_isAllowedSpriteTag(TAG * tag)
264 {
265     int id = tag->id;
266     int t=0;
267     while(swf_spritetagids[t]>=0)
268     {
269         if(swf_spritetagids[t] == id) 
270             return 1;
271         t++;
272     }
273     return 0; 
274 }
275
276 U8 swf_isDefiningTag(TAG * tag)
277 {
278     int id = tag->id;
279     int t=0;
280     while(swf_definingtagids[t]>=0)
281     {
282         if(swf_definingtagids[t] == id) 
283             return 1;
284         t++;
285     }
286     return 0; 
287 }
288
289 U8 swf_isPseudoDefiningTag(TAG * tag)
290 {
291     int id = tag->id;
292     int t=0;
293     while(swf_pseudodefiningtagids[t]>=0)
294     {
295         if(swf_pseudodefiningtagids[t] == id) 
296             return 1;
297         t++;
298     }
299     return 0; 
300 }
301
302 int swf_GetDepth(TAG * t)
303
304   int depth = -1;
305   U32 oldTagPos;
306   oldTagPos = swf_GetTagPos(t);
307   swf_SetTagPos(t,0);
308
309   switch (swf_GetTagID(t))
310   { case ST_PLACEOBJECT:
311     case ST_REMOVEOBJECT:
312       swf_GetU16(t); //id
313       depth = swf_GetU16(t);
314       break;
315     case ST_REMOVEOBJECT2:
316       depth = swf_GetU16(t);
317       break;
318     case ST_PLACEOBJECT2:
319     { U8 flags = swf_GetU8(t);
320       depth = swf_GetU16(t);
321     } break;
322     case ST_SETTABINDEX:
323     {
324       depth = swf_GetU16(t);
325     }
326   }
327   swf_SetTagPos(t,oldTagPos);
328   return depth;
329 }
330
331 void swf_SetDepth(TAG * t, U16 depth)
332
333   switch (swf_GetTagID(t))
334   { case ST_PLACEOBJECT:
335     case ST_REMOVEOBJECT:
336       PUT16(t->data, depth);
337       break;
338     case ST_REMOVEOBJECT2:
339       PUT16(t->data, depth);
340       break;
341     case ST_PLACEOBJECT2:
342       PUT16(&t->data[1], depth);
343       break;
344     case ST_SETTABINDEX:
345       PUT16(t->data, depth);
346       break;
347     default:
348       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
349   }
350 }
351
352 char* swf_GetName(TAG * t)
353 {
354     char* name = 0;
355     U32 oldTagPos;
356     MATRIX m;
357     CXFORM c;
358     oldTagPos = swf_GetTagPos(t);
359     swf_SetTagPos(t,0);
360     switch(swf_GetTagID(t))
361     {
362         case ST_FRAMELABEL:
363             name = &t->data[swf_GetTagPos(t)];
364         break;
365         case ST_PLACEOBJECT2: {   
366             U8 flags = swf_GetU8(t);
367             swf_GetU16(t); //depth;
368             if(flags&PF_CHAR) 
369               swf_GetU16(t); //id
370             if(flags&PF_MATRIX)
371               swf_GetMatrix(t, &m);
372             if(flags&PF_CXFORM)
373               swf_GetCXForm(t, &c, 1);
374             if(flags&PF_RATIO)
375               swf_GetU16(t);
376             if(flags&PF_CLIPACTION)
377               swf_GetU16(t);
378             if(flags&PF_NAME) {
379               swf_ResetReadBits(t);
380               name = &t->data[swf_GetTagPos(t)];
381             }
382         }
383         break;
384     }
385     swf_SetTagPos(t,oldTagPos);
386     return name;
387 }
388
389 /* used in enumerateUsedIDs */
390 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
391 {
392     GRADIENT dummy1;
393     GRADIENT dummy2;
394     int t;
395     if(!gradient1)
396         gradient1 = &dummy1;
397     if(!gradient2)
398         gradient2 = &dummy2;
399     gradient1->num = 
400     gradient2->num = swf_GetU8(tag);
401     for(t=0;t<gradient1->num;t++)
402     {
403         int s=t;
404         if(s>=8) //FIXME
405             s=7;
406         gradient1->ratios[t] = swf_GetU8(tag);
407         swf_GetRGBA(tag, &gradient1->rgba[t]);
408         gradient2->ratios[t] = swf_GetU8(tag);
409         swf_GetRGBA(tag, &gradient2->rgba[t]);
410     }
411 }
412
413 #define DEBUG_ENUMERATE if(0)
414
415 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
416 {
417     U16 count;
418     int t;
419     count = swf_GetU8(tag);
420     if(count == 0xff && num>1) // defineshape2,3 only
421         count = swf_GetU16(tag);
422
423     for(t=0;t<count;t++)
424     {
425         int type;
426         U8*pos;
427         swf_ResetReadBits(tag);
428         type = swf_GetU8(tag); //type
429         if(type == 0) {
430             if(num == 3)
431                 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
432             else 
433                 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
434         }
435         else if(type == 0x10 || type == 0x12)
436         {
437             swf_ResetReadBits(tag);
438             swf_GetMatrix(tag, NULL);
439             if(morph)
440                 swf_GetMatrix(tag, NULL);
441             swf_ResetReadBits(tag);
442             if(morph)
443                 swf_GetMorphGradient(tag, NULL, NULL);
444             else
445                 swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
446         }
447         else if(type == 0x40 || type == 0x41)
448         {
449             swf_ResetReadBits(tag);
450             // we made it.
451             if(tag->data[tag->pos] != 0xff ||
452                tag->data[tag->pos+1] != 0xff)
453             (callback)(tag, tag->pos, callback_data);
454
455             swf_GetU16(tag);
456             swf_ResetReadBits(tag);
457             swf_GetMatrix(tag, NULL);
458             if(morph)
459                 swf_GetMatrix(tag, NULL);
460         }
461         else {
462             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
463         }
464     }
465     swf_ResetReadBits(tag);
466     count = swf_GetU8(tag); // line style array
467     if(count == 0xff)
468         count = swf_GetU16(tag);
469     for(t=0;t<count;t++) 
470     {
471         swf_GetU16(tag);
472         if(morph)
473             swf_GetU16(tag);
474         if(num == 3)
475             {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
476         else
477             {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
478     }
479 }
480
481 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
482 {
483     int num = 1;
484     swf_ResetReadBits(tag);
485     tag->pos = 0;
486     switch(tag->id)
487     {
488         case ST_DEFINEBUTTONSOUND: {
489             int t;
490             callback(tag, tag->pos + base, callback_data);
491             for(t=0;t<4;t++) {
492                 int flags;
493                 callback(tag, tag->pos + base, callback_data);
494                 swf_GetU16(tag); //sound id
495                 flags = swf_GetU8(tag);
496                 if(flags&1)
497                     swf_GetU32(tag); // in point
498                 if(flags&2)
499                     swf_GetU32(tag); // out points
500                 if(flags&4)
501                     swf_GetU16(tag); // loop count
502                 if(flags&8)
503                 {
504                     int npoints = swf_GetU8(tag);
505                     int s;
506                     for(s=0;s<npoints;s++)
507                     {
508                         swf_GetU32(tag);
509                         swf_GetU16(tag);
510                         swf_GetU16(tag);
511                     }
512                 }
513             }
514         } break;
515         case ST_DEFINEBUTTONCXFORM:
516             callback(tag, tag->pos + base, callback_data); //button id
517         break;
518
519         case ST_EXPORTASSETS: {
520             int num =  swf_GetU16(tag);
521             int t;
522             for(t=0;t<num;t++) {
523                 callback(tag, tag->pos + base, callback_data); //button id
524                 swf_GetU16(tag); //id
525                 while(swf_GetU8(tag)); //name
526             }
527         } break;
528
529         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
530         case ST_NAMECHARACTER:
531         case ST_GENERATORTEXT:
532             callback(tag, tag->pos + base, callback_data);
533         break;
534         case ST_PLACEOBJECT:
535             callback(tag, tag->pos + base, callback_data);
536         break;
537         case ST_PLACEOBJECT2:
538             // only if placeflaghascharacter
539             if(!(tag->data[0]&2))
540                 break;
541             callback(tag, 3 + base, callback_data);
542         break;
543         case ST_REMOVEOBJECT:
544             callback(tag, tag->pos + base, callback_data);
545         break;
546         case ST_STARTSOUND:
547             callback(tag, tag->pos + base, callback_data);
548         break;
549         case ST_DEFINESPRITE: {
550             if(tag->len <= 4)
551                 break; // sprite is expanded
552
553             swf_GetU16(tag); // id
554             swf_GetU16(tag); // framenum
555
556             while(1) {
557                 U16 flags = swf_GetU16(tag);
558                 U32 len;
559                 U16 id = flags>>6;
560                 TAG *tag2 = swf_InsertTag(NULL, id);
561                 len = flags&0x3f;
562                 if(len == 63)
563                     len = swf_GetU32(tag);
564                 if(id == ST_END)
565                     break;
566                 tag2->len = tag2->memsize = len;
567                 tag2->data = rfx_alloc(len);
568                 memcpy(tag2->data, &tag->data[tag->pos], len);
569                 /* I never saw recursive sprites, but they are (theoretically) 
570                    possible, so better add base here again */
571                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
572                 swf_DeleteTag(tag2);
573                 swf_GetBlock(tag, NULL, len);
574             }
575         } 
576         break;
577         case ST_DEFINEBUTTON2: // has some font ids in the button records
578             num++; 
579         //fallthrough
580         case ST_DEFINEBUTTON: {
581             swf_GetU16(tag); //button id
582             if(num>1)
583             { 
584                 int offset;
585                 swf_GetU8(tag); //flag
586                 offset = swf_GetU16(tag); //offset
587             }
588             while(1)
589             {
590                 U16 charid;
591                 if(!swf_GetU8(tag)) //flags
592                     break; 
593                 callback(tag, tag->pos + base, callback_data);
594                 swf_GetU16(tag); //char
595                 swf_GetU16(tag); //layer
596                 swf_ResetReadBits(tag);
597                 swf_GetMatrix(tag, NULL);
598                 if(num>1) {
599                   swf_ResetReadBits(tag);
600                   swf_GetCXForm(tag, NULL, 1);
601                 }
602             }
603             // ...
604         }
605         break;
606         case ST_DEFINEEDITTEXT:  {
607             U8 flags1,flags2;
608             swf_GetU16(tag); //id
609             swf_GetRect(tag, NULL); //bounding box
610             swf_ResetReadBits(tag);
611             flags1 = swf_GetU8(tag);
612             flags2 = swf_GetU8(tag);
613             if(flags1 & 1)
614                 callback(tag, tag->pos + base, callback_data);
615         }
616         break;
617         case ST_DEFINETEXT2:
618             num ++;
619         case ST_DEFINETEXT: { 
620             int glyphbits, advancebits;
621             int id;
622             id = swf_GetU16(tag); //id
623             swf_GetRect(tag, NULL); //bounding box
624             swf_ResetReadBits(tag);
625             swf_GetMatrix(tag, NULL); //matrix
626             swf_ResetReadBits(tag);
627             glyphbits = swf_GetU8(tag); //glyphbits
628             advancebits = swf_GetU8(tag); //advancebits
629             while(1) {
630                 U16 flags;
631                 int t;
632                 swf_ResetReadBits(tag);
633                 flags = swf_GetBits(tag, 8);
634                 if(!flags) break;
635                 
636                 swf_ResetReadBits(tag);
637                 if(flags & 8) { // hasfont
638                     callback(tag, tag->pos + base, callback_data);
639                     id = swf_GetU16(tag);
640                 }
641                 if(flags & 4) { // hascolor
642                     if(num==1) swf_GetRGB(tag, NULL);
643                     else       swf_GetRGBA(tag, NULL);
644                 }
645                 if(flags & 2) { //has x offset
646                     swf_ResetReadBits(tag);
647                     swf_GetU16(tag);
648                 }
649                 if(flags & 1) { //has y offset
650                     swf_ResetReadBits(tag);
651                     swf_GetU16(tag);
652                 }
653                 if(flags & 8) { //has height
654                     swf_ResetReadBits(tag);
655                     swf_GetU16(tag);
656                 }
657                 
658                 flags = swf_GetBits(tag, 8);
659                 if(!flags) break;
660                 swf_ResetReadBits(tag);
661                 for(t=0;t<flags;t++) {
662                     swf_GetBits(tag, glyphbits);
663                     swf_GetBits(tag, advancebits);
664                 }
665             }
666             break;
667         }
668         case ST_GLYPHNAMES:
669         case ST_DEFINEFONTINFO:
670         case ST_DEFINEFONTINFO2:
671         case ST_VIDEOFRAME:
672             callback(tag, tag->pos + base, callback_data);
673         break;
674         case ST_DEFINEVIDEOSTREAM:
675         break;
676
677         case ST_DOINITACTION:
678             callback(tag, tag->pos + base, callback_data);
679         break;
680
681         case ST_DEFINEMORPHSHAPE:
682         case ST_DEFINESHAPE3:
683         num++; //fallthrough
684         case ST_DEFINESHAPE2:
685         num++; //fallthrough
686         case ST_DEFINESHAPE: {
687             int fillbits;
688             int linebits;
689             int id; 
690             int numshapes = 1;
691             int morph = 0;
692             if(tag->id == ST_DEFINEMORPHSHAPE) {
693                 numshapes = 2;
694                 morph = 1;
695             }
696
697             id = swf_GetU16(tag); // id;
698             swf_GetRect(tag, NULL); // bounds
699             if(morph) {
700                 swf_ResetReadBits(tag);
701                 swf_GetRect(tag, NULL); // bounds2
702                 swf_GetU32(tag); //offset to endedges
703             }
704    
705             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
706
707             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
708             DEBUG_ENUMERATE printf("-------\n");
709             while(--numshapes>=0) /* morph shapes define two shapes */
710             {
711                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
712                 fillbits = swf_GetBits(tag, 4);
713                 linebits = swf_GetBits(tag, 4);
714                 DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
715                 swf_ResetReadBits(tag);
716                 while(1) {
717                     int flags;
718                     flags = swf_GetBits(tag, 1);
719                     if(!flags) { //style change
720                         flags = swf_GetBits(tag, 5);
721                         if(!flags)
722                             break;
723                         if(flags&1) { //move
724                             int n = swf_GetBits(tag, 5); 
725                             int x,y;
726                             x = swf_GetBits(tag, n); //x
727                             y = swf_GetBits(tag, n); //y
728                             DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
729                         }
730                         if(flags&2) { //fill0
731                             int fill0;
732                             fill0 = swf_GetBits(tag, fillbits); 
733                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
734                         }
735                         if(flags&4) { //fill1
736                             int fill1;
737                             fill1 = swf_GetBits(tag, fillbits); 
738                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
739                         }
740                         if(flags&8) { //linestyle
741                             int line;
742                             line = swf_GetBits(tag, linebits); 
743                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
744                         }
745                         if(flags&16) {
746                             DEBUG_ENUMERATE printf("more fillstyles\n");
747                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
748                             fillbits = swf_GetBits(tag, 4);
749                             linebits = swf_GetBits(tag, 4);
750                         }
751                     } else {
752                         flags = swf_GetBits(tag, 1);
753                         if(flags) { //straight edge
754                             int n = swf_GetBits(tag, 4) + 2;
755                             if(swf_GetBits(tag, 1)) { //line flag
756                                 int x,y;
757                                 x = swf_GetSBits(tag, n); //delta x
758                                 y = swf_GetSBits(tag, n); //delta y
759                                 DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
760                             } else {
761                                 int v=swf_GetBits(tag, 1);
762                                 int d;
763                                 d = swf_GetSBits(tag, n); //vert/horz
764                                 DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
765                             }
766                         } else { //curved edge
767                             int n = swf_GetBits(tag, 4) + 2;
768                             int x1,y1,x2,y2;
769                             x1 = swf_GetSBits(tag, n);
770                             y1 = swf_GetSBits(tag, n);
771                             x2 = swf_GetSBits(tag, n);
772                             y2 = swf_GetSBits(tag, n);
773                             DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
774                         }
775                     }
776                 }
777             }
778         }
779         break;
780         default:
781         break;
782     }
783 }
784
785 void callbackCount(TAG * t,int pos, void*ptr)
786 {
787     (*(int*)ptr)++;
788     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
789 }
790
791 void callbackFillin(TAG * t,int pos, void*ptr)
792 {
793     **(int**)ptr = pos;
794     (*(int**)ptr)++;
795     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
796 }
797
798 int swf_GetNumUsedIDs(TAG * t)
799 {
800     int num = 0;
801     enumerateUsedIDs(t, 0, callbackCount, &num);
802     return num;
803 }
804
805 void swf_GetUsedIDs(TAG * t, int * positions)
806 {
807     int * ptr = positions;
808     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
809 }
810
811 void swf_Relocate (SWF*swf, char*bitmap)
812 {
813     TAG*tag;
814     int slaveids[65536];
815     memset(slaveids, -1, sizeof(slaveids));
816     tag = swf->firstTag;
817     while(tag)
818     {
819         int num; 
820         int *ptr;
821         int t;
822
823         if(swf_isDefiningTag(tag))
824         {
825             int newid;
826             int id;
827             
828             id = swf_GetDefineID(tag); //own id
829
830             if(!bitmap[id]) { //free
831                 newid = id;
832             }
833             else {
834                 newid = 0;
835                 for (t=1;t<65536;t++)
836                 {
837                     if(!bitmap[t])
838                     {
839                         newid = t;
840                         break;
841                     }
842                 }
843             }
844             bitmap[newid] = 1;
845             slaveids[id] = newid;
846
847             swf_SetDefineID(tag, newid);
848         } 
849         
850         num = swf_GetNumUsedIDs(tag);
851         if(num) {
852             ptr = rfx_alloc(sizeof(int)*num);
853             swf_GetUsedIDs(tag, ptr);
854
855             for(t=0;t<num;t++) {
856                 int id = GET16(&tag->data[ptr[t]]);
857                 if(slaveids[id]<0) {
858                     fprintf(stderr, "swf_Relocate: Mapping id (%d) never encountered before in %s\n", id,
859                             swf_TagGetName(tag));
860                     return ;
861                 }
862                 id = slaveids[id];
863                 PUT16(&tag->data[ptr[t]], id);
864             }
865         }
866         tag=tag->next;
867     }
868 }
869
870 /* untested */
871 void swf_Relocate2(SWF*swf, int*id2id)
872 {
873     TAG*tag;
874     tag = swf->firstTag;
875     while(tag) {
876         if(swf_isDefiningTag(tag)) {
877             int id = swf_GetDefineID(tag);
878             id = id2id[id];
879             if(id>=0) {
880                 swf_SetDefineID(tag, id);
881             }
882         }
883         int num = swf_GetNumUsedIDs(tag);
884         if(num) {
885             int *ptr;
886             int t;
887             ptr = rfx_alloc(sizeof(int)*num);
888             swf_GetUsedIDs(tag, ptr);
889             for(t=0;t<num;t++) {
890                 int id = GET16(&tag->data[ptr[t]]);
891                 id = id2id[id];
892                 if(id>=0) {
893                     PUT16(&tag->data[ptr[t]], id);
894                 }
895             }
896         }
897     }
898 }
899
900 void swf_RelocateDepth(SWF*swf, char*bitmap)
901 {
902     TAG*tag;
903     int nr;
904     tag = swf->firstTag;
905     for(nr=65535;nr>=0;nr--) {
906         if(bitmap[nr] != 0) 
907             break;
908     }
909     // now nr is the highest used depth. So we start
910     // assigning depths at nr+1
911     nr++;
912
913     while(tag)
914     {
915         int depth;
916         /* TODO * clip depths 
917                 * sprites
918          */
919         if(tag->id == ST_PLACEOBJECT2) {
920             SWFPLACEOBJECT obj;
921             swf_GetPlaceObject(tag, &obj);
922             if(obj.clipdepth) {
923                 int newdepth = obj.clipdepth+nr;
924                 if(newdepth>65535) {
925                     fprintf(stderr, "Couldn't relocate depths: too large values\n");
926                     newdepth = 65535;
927                 }
928                 obj.clipdepth = newdepth;
929                 swf_ResetTag(tag, ST_PLACEOBJECT2);
930                 swf_SetPlaceObject(tag, &obj);
931             }
932             swf_PlaceObjectFree(&obj);
933         }
934
935         depth = swf_GetDepth(tag);
936         if(depth>=0) {
937             int newdepth = depth+nr;
938             if(newdepth>65535) {
939                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
940                 newdepth = 65535;
941             }
942             swf_SetDepth(tag, newdepth);
943         }
944         tag=tag->next;
945     }
946 }
947
948 U8 swf_isShapeTag(TAG*tag)
949 {
950     if(tag->id == ST_DEFINESHAPE ||
951        tag->id == ST_DEFINESHAPE2 ||
952        tag->id == ST_DEFINESHAPE3) 
953         return 1;
954     return 0;
955 }
956
957 U8 swf_isPlaceTag(TAG*tag)
958 {
959     if(tag->id == ST_PLACEOBJECT ||
960        tag->id == ST_PLACEOBJECT2)
961         return 1;
962     return 0;
963 }
964 U8 swf_isTextTag(TAG*tag)
965 {
966     if(tag->id == ST_DEFINETEXT ||
967        tag->id == ST_DEFINETEXT2)
968         return 1;
969     return 0;
970 }
971
972 U8 swf_isFontTag(TAG*tag)
973 {
974     if(tag->id == ST_DEFINEFONT ||
975        tag->id == ST_DEFINEFONT2 ||
976        tag->id == ST_DEFINEFONTINFO)
977         return 1;
978     return 0;
979 }
980
981 U8  swf_isImageTag(TAG*tag)
982 {
983     if(tag->id == ST_DEFINEBITSJPEG || 
984        tag->id == ST_DEFINEBITSJPEG2 || 
985        tag->id == ST_DEFINEBITSJPEG3 ||
986        tag->id == ST_DEFINEBITSLOSSLESS || 
987        tag->id == ST_DEFINEBITSLOSSLESS2)
988         return 1;
989     return 0;
990 }
991
992 TAG* swf_Concatenate (TAG*list1,TAG*list2)
993 {
994     TAG*tag=0,*lasttag=0;
995     char bitmap[65536];
996     char depthmap[65536];
997     SWF swf1,swf2;
998     memset(bitmap, 0, sizeof(bitmap));
999     memset(depthmap, 0, sizeof(depthmap));
1000     memset(&swf1, 0, sizeof(swf1));
1001     memset(&swf2, 0, sizeof(swf2));
1002
1003     swf1.firstTag = list1;
1004     swf_FoldAll(&swf1);
1005     swf2.firstTag = list2;
1006     swf_FoldAll(&swf2);
1007
1008     tag = list1;
1009     while(tag) {
1010         if(!swf_isDefiningTag(tag)) {
1011             int id = swf_GetDefineID(tag);
1012             bitmap[id] = 1;
1013         }
1014         if(tag->id == ST_PLACEOBJECT ||
1015            tag->id == ST_PLACEOBJECT2) {
1016             int depth = swf_GetDepth(tag);
1017             depthmap[depth] = 1;
1018         }
1019         if(tag->id == ST_REMOVEOBJECT ||
1020            tag->id == ST_REMOVEOBJECT2) {
1021             int depth = swf_GetDepth(tag);
1022             depthmap[depth] = 0;
1023         }
1024         tag = tag->next;
1025         lasttag = tag;
1026     }
1027     swf_Relocate(&swf2, bitmap);
1028     swf_RelocateDepth(&swf2, depthmap);
1029     lasttag->next = swf2.firstTag;
1030     swf2.firstTag->prev = lasttag;
1031
1032     return swf1.firstTag;
1033 }
1034
1035 static int tagHash(TAG*tag)
1036 {
1037     int t, h=0;
1038     unsigned int a = 0x6b973e5a;
1039     /* start at pos 2, as 0 and 1 are the id */
1040     for(t=2;t<tag->len;t++) {
1041         unsigned int b = a;
1042         a >>= 8;
1043         a += tag->data[t]*0xefbc35a5*b*(t+1);
1044     }
1045     return a&0x7fffffff; //always return positive number
1046 }
1047
1048 void swf_Optimize(SWF*swf)
1049 {
1050     const int hash_size = 131072;
1051     char* dontremap = rfx_calloc(sizeof(char)*65536);
1052     U16* remap = rfx_alloc(sizeof(U16)*65536);
1053     TAG* id2tag = rfx_calloc(sizeof(TAG*)*65536);
1054     TAG** hashmap = rfx_calloc(sizeof(TAG*)*hash_size);
1055     TAG* tag;
1056     int t;
1057     for(t=0;t<65536;t++) {
1058         remap[t] = t;
1059     }
1060
1061     swf_FoldAll(swf);
1062
1063     tag = swf->firstTag;
1064     while(tag) {
1065         /* make sure we don't remap to this tag,
1066            as it might have different "helper tags" 
1067            FIXME: a better way would be to compare
1068                   the helper tags, too.
1069          */
1070         if(swf_isPseudoDefiningTag(tag) &&
1071            tag->id != ST_NAMECHARACTER) {
1072             dontremap[swf_GetDefineID(tag)] = 1;
1073         }
1074         tag=tag->next;
1075     }
1076     tag = swf->firstTag;
1077     while(tag) {
1078         TAG*next = tag->next;
1079
1080         /* remap the tag */
1081         int num = swf_GetNumUsedIDs(tag);
1082         int*positions = rfx_alloc(sizeof(int)*num);
1083         int t;
1084         swf_GetUsedIDs(tag, positions);
1085         for(t=0;t<num;t++) {
1086             int id = GET16(&tag->data[positions[t]]);
1087             id = remap[id];
1088             PUT16(&tag->data[positions[t]], id);
1089         }
1090         rfx_free(positions);
1091
1092         /* now look for previous tags with the same
1093            content */
1094         if(swf_isDefiningTag(tag)) {
1095             TAG*tag2;
1096             int id = swf_GetDefineID(tag);
1097             int hash = tagHash(tag);
1098             int match=0;
1099             if(!dontremap[id]) 
1100             while((tag2 = hashmap[hash%hash_size])) {
1101                 if(tag2 != (TAG*)0 && tag->len == tag2->len) {
1102                     if(memcmp(&tag->data[2],&tag2->data[2],tag->len-2) == 0) {
1103                         match = 1;
1104                         break;
1105                     }
1106                 }
1107                 hash++;
1108             }
1109             if(!match) {
1110                 while(hashmap[hash%hash_size]) hash++;
1111                 hashmap[hash%hash_size] = tag;
1112             } else {
1113                 /* we found two identical tags- remap one
1114                    of them */
1115                 remap[id] = swf_GetDefineID(tag2);
1116                 swf_DeleteTag(tag);
1117                 if(tag == swf->firstTag)
1118                     swf->firstTag = next;
1119             }
1120         } else if(swf_isPseudoDefiningTag(tag)) {
1121             int id = swf_GetDefineID(tag);
1122             if(remap[id]!=id) {
1123                 /* if this tag was remapped, we don't
1124                    need the helper tag anymore. Discard
1125                    it. */
1126                 swf_DeleteTag(tag);
1127                 if(tag == swf->firstTag)
1128                     swf->firstTag = next;
1129             }
1130         }
1131
1132         tag = next;
1133     }
1134     
1135     rfx_free(dontremap);
1136     rfx_free(remap);
1137     rfx_free(id2tag);
1138     rfx_free(hashmap);
1139 }
1140
1141 void swf_SetDefineBBox(TAG * tag, SRECT newbbox)
1142 {
1143     U16 id = 0;
1144     SRECT b1;
1145     swf_SetTagPos(tag,0);
1146
1147     switch (swf_GetTagID(tag))
1148     { 
1149         case ST_DEFINESHAPE:
1150         case ST_DEFINESHAPE2:
1151         case ST_DEFINESHAPE3:
1152         case ST_DEFINEEDITTEXT:
1153         case ST_DEFINETEXT:
1154         case ST_DEFINETEXT2:
1155         case ST_DEFINEVIDEOSTREAM: {
1156               U32 after_bbox_offset = 0, len;
1157               U8*data;
1158               id = swf_GetU16(tag);
1159               swf_GetRect(tag, &b1);
1160               swf_ResetReadBits(tag);
1161               after_bbox_offset = tag->pos;
1162               len = tag->len - after_bbox_offset;
1163               data = malloc(len);
1164               memcpy(data, &tag->data[after_bbox_offset], len);
1165               tag->writeBit = 0;
1166               tag->len = 2;
1167               swf_SetRect(tag, &newbbox);
1168               swf_SetBlock(tag, data, len);
1169               free(data);
1170               tag->pos = tag->readBit = 0;
1171
1172         } break;
1173         default:
1174             fprintf(stderr, "rfxswf: Tag %d (%s) has no bbox\n", tag->id, swf_TagGetName(tag));
1175     }
1176 }
1177
1178 RGBA swf_GetSWFBackgroundColor(SWF*swf)
1179 {
1180     TAG*t=swf->firstTag;
1181     RGBA color;
1182     color.r = color.b = color.g = 0;
1183     color.a = 255;
1184     while(t) {
1185         if(t->id == ST_SETBACKGROUNDCOLOR) {
1186             swf_SetTagPos(t, 0);
1187             color.r = swf_GetU8(t);
1188             color.g = swf_GetU8(t);
1189             color.b = swf_GetU8(t);
1190             break;
1191         }
1192         t=t->next;
1193     }
1194     return color;
1195 }
1196