added definemorphshape2 to getdefineid
[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_DEFINEMORPHSHAPE2:
104     case ST_DEFINEEDITTEXT:
105     case ST_DEFINEBITS:
106     case ST_DEFINEBITSJPEG2:
107     case ST_DEFINEBITSJPEG3:
108     case ST_DEFINEBITSLOSSLESS:
109     case ST_DEFINEBITSLOSSLESS2:
110     case ST_DEFINESCALINGGRID: //pseudodefine
111     case ST_DEFINEBUTTON:
112     case ST_DEFINEBUTTON2:
113     case ST_DEFINEBUTTONCXFORM: //pseudodefine
114     case ST_DEFINEBUTTONSOUND: //pseudodefine
115     case ST_CSMTEXTSETTINGS: //pseudodefine
116     case ST_DEFINEFONT:
117     case ST_DEFINEFONT2:
118     case ST_DEFINEFONT3:
119     case ST_DEFINEFONTINFO: //pseudodefine
120     case ST_DEFINEFONTINFO2: //pseudodefine
121     case ST_DEFINEFONTALIGNZONES: //pseudodefine
122     case ST_DEFINEFONTNAME: //pseudodefine
123     case ST_DEFINETEXT:
124     case ST_DEFINETEXT2:
125     case ST_DEFINESOUND:
126     case ST_DEFINESPRITE:
127     case ST_DEFINEMOVIE:
128     case ST_DEFINEVIDEOSTREAM:
129     case ST_GLYPHNAMES: //pseudodefine
130     case ST_VIDEOFRAME: //pseudodefine
131     case ST_NAMECHARACTER: //pseudodefine
132     case ST_DOINITACTION: //pseudodefine
133       id = swf_GetU16(t);
134       break;
135     default:
136       fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
137   }
138
139   swf_SetTagPos(t,oldTagPos);
140
141   return id;
142 }
143
144 SRECT swf_GetDefineBBox(TAG * t)
145 {
146   U32 oldTagPos;
147   U16 id = 0;
148   SRECT b1,b2;
149   memset(&b1, 0, sizeof(b1));
150
151   oldTagPos = swf_GetTagPos(t);
152   swf_SetTagPos(t,0);
153
154   swf_GetRect(0, &b1);
155
156   switch (swf_GetTagID(t))
157   { case ST_DEFINESHAPE:
158     case ST_DEFINESHAPE2:
159     case ST_DEFINESHAPE3:
160     case ST_DEFINESHAPE4:
161     case ST_DEFINEEDITTEXT:
162     case ST_DEFINETEXT:
163     case ST_DEFINETEXT2:
164     case ST_DEFINEVIDEOSTREAM:
165       id = swf_GetU16(t);
166       swf_GetRect(t, &b1);
167       break;
168     case ST_DEFINEMORPHSHAPE:
169       id = swf_GetU16(t);
170       swf_GetRect(t, &b1);
171       swf_GetRect(t, &b2);
172       swf_ExpandRect2(&b1, &b2);
173       break;
174     case ST_DEFINEBITSLOSSLESS:
175     case ST_DEFINEBITSLOSSLESS2:
176     case ST_DEFINEBITS:
177     case ST_DEFINEBITSJPEG2:
178     case ST_DEFINEBITSJPEG3:
179       // FIXME
180       break;
181   }
182
183   swf_SetTagPos(t,oldTagPos);
184
185   return b1;
186 }
187
188 U16 swf_GetPlaceID(TAG * t)
189 // up to SWF 4.0
190 { U32 oldTagPos;
191   U16 id = 0;
192
193   oldTagPos = swf_GetTagPos(t);
194   swf_SetTagPos(t,0);
195
196   switch (swf_GetTagID(t))
197   { case ST_PLACEOBJECT:
198     case ST_REMOVEOBJECT:
199     case ST_FREECHARACTER:
200     case ST_STARTSOUND:
201       id = swf_GetU16(t);
202       break;
203
204     case ST_PLACEOBJECT2:
205     { U8 flags = swf_GetU8(t);
206       U16 d = swf_GetU16(t);
207       id = (flags&PF_CHAR)?swf_GetU16(t):id;
208     } break;
209     case ST_PLACEOBJECT3:
210     { U8 flags = swf_GetU8(t);
211       U8 flags2 = swf_GetU8(t);
212       U16 d = swf_GetU16(t);
213       id = (flags&PF_CHAR)?swf_GetU16(t):id;
214     } break;
215
216   }
217
218   swf_SetTagPos(t,oldTagPos);
219
220   return id;
221 }
222
223 static int swf_definingtagids[] =
224 {ST_DEFINESHAPE,
225  ST_DEFINESHAPE2,
226  ST_DEFINESHAPE3,
227  ST_DEFINESHAPE4,
228  ST_DEFINEMORPHSHAPE,
229  ST_DEFINEMORPHSHAPE2,
230  ST_DEFINEFONT,
231  ST_DEFINEFONT2,
232  ST_DEFINEFONT3,
233  ST_DEFINETEXT,
234  ST_DEFINETEXT2,
235  ST_DEFINEEDITTEXT,
236  ST_DEFINEBITS,
237  ST_DEFINEBITSJPEG2,
238  ST_DEFINEBITSJPEG3,
239  ST_DEFINEBITSLOSSLESS,
240  ST_DEFINEBITSLOSSLESS2,
241  ST_DEFINEMOVIE,
242  ST_DEFINESPRITE,
243  ST_DEFINEBUTTON,
244  ST_DEFINEBUTTON2,
245  ST_DEFINESOUND,
246  ST_DEFINEVIDEOSTREAM,
247  ST_DEFINEBINARY,
248  -1
249 };
250
251 // tags which may be used inside a sprite definition
252 static int swf_spritetagids[] =
253 {ST_SHOWFRAME,
254  ST_PLACEOBJECT,
255  ST_PLACEOBJECT2,
256  ST_PLACEOBJECT3,
257  ST_REMOVEOBJECT,
258  ST_REMOVEOBJECT2,
259  ST_DOACTION,
260  ST_DOABC,
261  ST_STARTSOUND,
262  ST_FRAMELABEL,
263  ST_SOUNDSTREAMHEAD,
264  ST_SOUNDSTREAMHEAD2,
265  ST_SOUNDSTREAMBLOCK,
266  ST_END,
267  -1
268 };
269
270 /* tags which add content or information to a character with a given ID */
271 static int swf_pseudodefiningtagids[] = 
272 {
273  ST_DEFINEFONTINFO,
274  ST_DEFINEFONTINFO2,
275  ST_DEFINEFONTALIGNZONES,
276  ST_DEFINEFONTNAME,
277  ST_DEFINEBUTTONCXFORM,
278  ST_DEFINEBUTTONSOUND,
279  ST_DEFINESCALINGGRID,
280  ST_CSMTEXTSETTINGS,
281  ST_NAMECHARACTER,
282  ST_DOINITACTION,
283  ST_VIDEOFRAME,
284  ST_GLYPHNAMES,
285  -1
286 };
287
288 U8 swf_isAllowedSpriteTag(TAG * tag)
289 {
290     int id = tag->id;
291     int t=0;
292     while(swf_spritetagids[t]>=0)
293     {
294         if(swf_spritetagids[t] == id) 
295             return 1;
296         t++;
297     }
298     return 0; 
299 }
300
301 U8 swf_isDefiningTag(TAG * tag)
302 {
303     int id = tag->id;
304     int t=0;
305     while(swf_definingtagids[t]>=0)
306     {
307         if(swf_definingtagids[t] == id) 
308             return 1;
309         t++;
310     }
311     return 0; 
312 }
313
314 U8 swf_isPseudoDefiningTag(TAG * tag)
315 {
316     int id = tag->id;
317     int t=0;
318     while(swf_pseudodefiningtagids[t]>=0)
319     {
320         if(swf_pseudodefiningtagids[t] == id) 
321             return 1;
322         t++;
323     }
324     return 0; 
325 }
326
327 int swf_GetDepth(TAG * t)
328
329   int depth = -1;
330   U32 oldTagPos;
331   oldTagPos = swf_GetTagPos(t);
332   swf_SetTagPos(t,0);
333
334   switch (swf_GetTagID(t))
335   { case ST_PLACEOBJECT:
336     case ST_REMOVEOBJECT:
337       swf_GetU16(t); //id
338       depth = swf_GetU16(t);
339       break;
340     case ST_REMOVEOBJECT2:
341       depth = swf_GetU16(t);
342       break;
343     case ST_PLACEOBJECT2:
344     { U8 flags = swf_GetU8(t);
345       depth = swf_GetU16(t);
346     } break;
347     case ST_PLACEOBJECT3:
348     { U8 flags = swf_GetU8(t);
349       U8 flags2 = swf_GetU8(t);
350       depth = swf_GetU16(t);
351     } break;
352     case ST_SETTABINDEX:
353     {
354       depth = swf_GetU16(t);
355     }
356   }
357   swf_SetTagPos(t,oldTagPos);
358   return depth;
359 }
360
361 void swf_SetDepth(TAG * t, U16 depth)
362
363   switch (swf_GetTagID(t))
364   { case ST_PLACEOBJECT:
365     case ST_REMOVEOBJECT:
366       PUT16(t->data, depth);
367       break;
368     case ST_REMOVEOBJECT2:
369       PUT16(t->data, depth);
370       break;
371     case ST_PLACEOBJECT2:
372       PUT16(&t->data[1], depth);
373       break;
374     case ST_SETTABINDEX:
375       PUT16(t->data, depth);
376       break;
377     default:
378       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
379   }
380 }
381
382 char* swf_GetName(TAG * t)
383 {
384     char* name = 0;
385     U32 oldTagPos;
386     MATRIX m;
387     CXFORM c;
388     oldTagPos = swf_GetTagPos(t);
389     swf_SetTagPos(t,0);
390     switch(swf_GetTagID(t))
391     {
392         case ST_FRAMELABEL:
393             name = (char*)&t->data[swf_GetTagPos(t)];
394         break;
395         case ST_PLACEOBJECT3:
396         case ST_PLACEOBJECT2: {   
397             U8 flags = swf_GetU8(t);
398             if(t->id == ST_PLACEOBJECT3)
399                 swf_GetU8(t);
400             swf_GetU16(t); //depth;
401             if(flags&PF_CHAR) 
402               swf_GetU16(t); //id
403             if(flags&PF_MATRIX)
404               swf_GetMatrix(t, &m);
405             if(flags&PF_CXFORM)
406               swf_GetCXForm(t, &c, 1);
407             if(flags&PF_RATIO)
408               swf_GetU16(t);
409             if(flags&PF_CLIPDEPTH)
410               swf_GetU16(t);
411             if(flags&PF_NAME) {
412               swf_ResetReadBits(t);
413               name = (char*)&t->data[swf_GetTagPos(t)];
414             }
415         }
416         break;
417     }
418     swf_SetTagPos(t,oldTagPos);
419     return name;
420 }
421
422 /* used in enumerateUsedIDs */
423 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
424 {
425     int t;
426     int num = swf_GetU8(tag) & 15;
427     if(gradient1) gradient1->num = num;
428     if(gradient2) gradient2->num = num;
429     
430     if(gradient1) {
431         gradient1->num = num;
432         gradient1->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient1->num);
433         gradient1->ratios = (U8*)rfx_calloc(sizeof(gradient1->ratios[0])*gradient1->num);
434     }
435     if(gradient2) {
436         gradient2->num = num;
437         gradient2->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient2->num);
438         gradient2->ratios = (U8*)rfx_calloc(sizeof(gradient2->ratios[0])*gradient2->num);
439     }
440     for(t=0;t<num;t++)
441     {
442         U8 ratio;
443         RGBA color;
444         
445         ratio = swf_GetU8(tag);
446         swf_GetRGBA(tag, &color);
447         if(gradient1) {
448             gradient1->ratios[t] = ratio;
449             gradient1->rgba[t] = color;
450         }
451
452         ratio = swf_GetU8(tag);
453         swf_GetRGBA(tag, &color);
454         if(gradient2) {
455             gradient2->ratios[t] = ratio;
456             gradient2->rgba[t] = color;
457         }
458     }
459 }
460
461 #define DEBUG_ENUMERATE if(0)
462 //#define DEBUG_ENUMERATE
463
464 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
465 {
466     U16 count;
467     int t;
468     count = swf_GetU8(tag);
469     if(count == 0xff && num>1) // defineshape2,3,4 only
470         count = swf_GetU16(tag);
471
472     DEBUG_ENUMERATE printf("%d fill styles\n", count);
473     for(t=0;t<count;t++)
474     {
475         int type;
476         type = swf_GetU8(tag); //type
477         DEBUG_ENUMERATE printf("fill style %d) %02x (tagpos=%d)\n", t, type, tag->pos);
478         if(type == 0) {
479             if(num >= 3)
480                 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
481             else 
482                 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
483         }
484         else if(type == 0x10 || type == 0x12 || type == 0x13)
485         {
486             swf_ResetReadBits(tag);
487             MATRIX m;
488             swf_GetMatrix(tag, &m);
489             DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
490             if(morph)
491                 swf_GetMatrix(tag, NULL);
492             swf_ResetReadBits(tag);
493             if(morph)
494                 swf_GetMorphGradient(tag, NULL, NULL);
495             else {
496                 GRADIENT g;
497                 swf_GetGradient(tag, &g, /*alpha*/ num>=3?1:0);
498                 DEBUG_ENUMERATE swf_DumpGradient(stdout, &g);
499                 if(type == 0x13)
500                     swf_GetU16(tag);
501             }
502         }
503         else if(type == 0x40 || type == 0x41 || type == 0x42 || type == 0x43)
504         {
505             swf_ResetReadBits(tag);
506             if(tag->data[tag->pos] != 0xff ||
507                tag->data[tag->pos+1] != 0xff)
508             (callback)(tag, tag->pos, callback_data);
509
510             swf_GetU16(tag);
511             swf_ResetReadBits(tag);
512             swf_GetMatrix(tag, NULL);
513             if(morph)
514                 swf_GetMatrix(tag, NULL);
515         }
516         else {
517             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x in tag %02x\n",type, tag->id);
518         }
519     }
520     swf_ResetReadBits(tag);
521     count = swf_GetU8(tag); // line style array
522     if(count == 0xff)
523         count = swf_GetU16(tag);
524     DEBUG_ENUMERATE printf("%d line styles\n", count);
525     for(t=0;t<count;t++) 
526     {
527         U16  width;
528         RGBA color;
529         width = swf_GetU16(tag);
530         if(morph)
531             swf_GetU16(tag);
532         if(num >= 4) {
533             U16 flags = swf_GetU16(tag);
534             if(flags & 0x2000)
535                 swf_GetU16(tag); // miter limit
536             if(flags & 0x0800) {
537                 fprintf(stderr, "Filled strokes parsing not yet supported\n");
538             }
539         }
540         if(num >= 3)
541             {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
542         else
543             {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
544         DEBUG_ENUMERATE printf("line style %d: %02x%02x%02x%02x \n", t, color.r,color.g,color.b,color.a);
545     }
546 }
547
548 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
549 {
550     int num = 1;
551     swf_ResetReadBits(tag);
552     tag->pos = 0;
553     switch(tag->id)
554     {
555         case ST_DEFINEBUTTONSOUND: {
556             int t;
557             callback(tag, tag->pos + base, callback_data);
558             for(t=0;t<4;t++) {
559                 int flags;
560                 callback(tag, tag->pos + base, callback_data);
561                 swf_GetU16(tag); //sound id
562                 flags = swf_GetU8(tag);
563                 if(flags&1)
564                     swf_GetU32(tag); // in point
565                 if(flags&2)
566                     swf_GetU32(tag); // out points
567                 if(flags&4)
568                     swf_GetU16(tag); // loop count
569                 if(flags&8)
570                 {
571                     int npoints = swf_GetU8(tag);
572                     int s;
573                     for(s=0;s<npoints;s++)
574                     {
575                         swf_GetU32(tag);
576                         swf_GetU16(tag);
577                         swf_GetU16(tag);
578                     }
579                 }
580             }
581         } break;
582         case ST_DEFINEBUTTONCXFORM:
583             callback(tag, tag->pos + base, callback_data); //button id
584         break;
585
586         case ST_EXPORTASSETS: {
587             int num =  swf_GetU16(tag);
588             int t;
589             for(t=0;t<num;t++) {
590                 callback(tag, tag->pos + base, callback_data); //button id
591                 swf_GetU16(tag); //id
592                 while(swf_GetU8(tag)); //name
593             }
594         } break;
595
596         case ST_IMPORTASSETS: 
597         case ST_IMPORTASSETS2: {
598             swf_GetString(tag); //count
599             swf_GetU8(tag); //reserved
600             swf_GetU8(tag); //reserved
601             int num =  swf_GetU16(tag); //url
602             int t;
603             for(t=0;t<num;t++) {
604                 callback(tag, tag->pos + base, callback_data); //button id
605                 swf_GetU16(tag); //id
606                 while(swf_GetU8(tag)); //name
607             }
608         } break;
609
610         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
611         case ST_NAMECHARACTER:
612         case ST_DEFINEBINARY:
613         case ST_DEFINEFONTNAME:
614         case ST_GENERATORTEXT:
615             callback(tag, tag->pos + base, callback_data);
616         break;
617         case ST_PLACEOBJECT:
618             callback(tag, tag->pos + base, callback_data);
619         break;
620         case ST_PLACEOBJECT2:
621             // only if placeflaghascharacter
622             if(!(tag->data[0]&2))
623                 break;
624             callback(tag, 3 + base, callback_data);
625         break;
626         case ST_PLACEOBJECT3:
627             // only if placeflaghascharacter
628             if(!(tag->data[0]&2))
629                 break;
630             callback(tag, 4 + base, callback_data);
631         break;
632         case ST_REMOVEOBJECT:
633             callback(tag, tag->pos + base, callback_data);
634         break;
635         case ST_STARTSOUND:
636             callback(tag, tag->pos + base, callback_data);
637         break;
638         case ST_DEFINESPRITE: {
639             if(tag->len <= 4)
640                 break; // sprite is expanded
641
642             swf_GetU16(tag); // id
643             swf_GetU16(tag); // framenum
644
645             while(1) {
646                 U16 flags = swf_GetU16(tag);
647                 U32 len;
648                 U16 id = flags>>6;
649                 TAG *tag2 = swf_InsertTag(NULL, id);
650                 len = flags&0x3f;
651                 if(len == 63)
652                     len = swf_GetU32(tag);
653                 if(id == ST_END)
654                     break;
655                 tag2->len = tag2->memsize = len;
656                 tag2->data = (U8*)rfx_alloc(len);
657                 memcpy(tag2->data, &tag->data[tag->pos], len);
658                 /* I never saw recursive sprites, but they are (theoretically) 
659                    possible, so better add base here again */
660                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
661                 swf_DeleteTag(tag2);
662                 swf_GetBlock(tag, NULL, len);
663             }
664         } 
665         break;
666         case ST_DEFINEBUTTON2: // has some font ids in the button records
667             num++; 
668         //fallthrough
669         case ST_DEFINEBUTTON: {
670             swf_GetU16(tag); //button id
671             if(num>1)
672             { 
673                 int offset;
674                 swf_GetU8(tag); //flag
675                 offset = swf_GetU16(tag); //offset
676             }
677             while(1)
678             {
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