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