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