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