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