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