new functions swf_SetDefineID, swf_Relocate.
[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 file is distributed under the GPL, see file COPYING for details 
11
12 */
13
14 // Matrix & Math tools for SWF files
15
16 #define S64 long long
17 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
18 { S64 a;
19   a = (S64)a1*(S64)b1+(S64)a2*(S64)b2;
20   return (SFIXED)(a>>16);
21 }
22 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
23 { S64 z = zaehler<<16;
24   S64 a = z/(S64)nenner;
25   return (SFIXED)a;
26 }
27 #undef S64
28
29 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
30 {        
31   if (!d) return NULL;
32   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
33   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
34   
35   d->tx = s1->tx + s2->tx;
36   d->ty = s1->ty + s2->ty;
37   
38   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
39   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
40   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
41   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
42
43   //DumpMatrix(NULL,d);
44   
45   return d;
46 }
47
48 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
49                                int x1,int y1,int x2,int y2)
50 { int dx1 = x1 - x0;
51   int dy1 = y1 - y0;
52   int dx2 = x2 - x0;
53   int dy2 = y2 - y0;
54   
55   if (!m) return NULL;
56   if ((!dx)||(!dy)) return NULL; // check DIV by zero
57
58   m->tx = x0;
59   m->ty = y0;
60   m->sx = RFXSWF_QFIX(dx1,dx);
61   m->sy = RFXSWF_QFIX(dy2,dy);
62   m->r0 = RFXSWF_QFIX(dy1,dx);
63   m->r1 = RFXSWF_QFIX(dx2,dy);
64   
65   return m;
66 }
67
68 void swf_SetDefineID(TAG * tag, U16 newid)
69 {
70   int oldlen = tag->len;
71   tag->len = 0;
72   swf_SetU16(tag, newid); /* set defining ID */
73   tag->len = oldlen;
74 }
75
76 U16 swf_GetDefineID(TAG * t)
77 // up to SWF 4.0
78 { U32 oldTagPos;
79   U16 id = 0;
80
81   oldTagPos = swf_GetTagPos(t);
82   swf_SetTagPos(t,0);
83
84   switch (swf_GetTagID(t))
85   { case ST_DEFINESHAPE:
86     case ST_DEFINESHAPE2:
87     case ST_DEFINESHAPE3:
88     case ST_DEFINEMORPHSHAPE:
89     case ST_DEFINEEDITTEXT:
90     case ST_DEFINEBITS:
91     case ST_DEFINEBITSJPEG2:
92     case ST_DEFINEBITSJPEG3:
93     case ST_DEFINEBITSLOSSLESS:
94     case ST_DEFINEBITSLOSSLESS2:
95     case ST_DEFINEBUTTON:
96     case ST_DEFINEBUTTON2:
97     case ST_DEFINEBUTTONCXFORM: //pseudodefine
98     case ST_DEFINEBUTTONSOUND: //pseudodefine
99     case ST_DEFINEFONT:
100     case ST_DEFINEFONT2:
101     case ST_DEFINEFONTINFO: //pseudodefine
102     case ST_DEFINETEXT:
103     case ST_DEFINETEXT2:
104     case ST_DEFINESOUND:
105     case ST_DEFINESPRITE:
106     case ST_NAMECHARACTER: //pseudodefine
107       id = swf_GetU16(t);
108       break;
109   }
110
111   swf_SetTagPos(t,oldTagPos);
112
113   return id;
114 }
115
116 U16 swf_GetPlaceID(TAG * t)
117 // up to SWF 4.0
118 { U32 oldTagPos;
119   U16 id = 0;
120
121   oldTagPos = swf_GetTagPos(t);
122   swf_SetTagPos(t,0);
123
124   switch (swf_GetTagID(t))
125   { case ST_PLACEOBJECT:
126     case ST_REMOVEOBJECT:
127     case ST_STARTSOUND:
128       id = swf_GetU16(t);
129       break;
130
131     case ST_PLACEOBJECT2:
132     { U8 flags = swf_GetU8(t);
133       U16 d = swf_GetU16(t);
134       id = (flags&PF_CHAR)?swf_GetU16(t):id;
135     } break;
136
137   }
138
139   swf_SetTagPos(t,oldTagPos);
140
141   return id;
142 }
143
144 static int swf_definingtagids[] =
145 {ST_DEFINESHAPE,
146  ST_DEFINESHAPE2,
147  ST_DEFINESHAPE3,
148  ST_DEFINEMORPHSHAPE,
149  ST_DEFINEFONT,
150  ST_DEFINEFONT2,
151  ST_DEFINETEXT,
152  ST_DEFINETEXT2,
153  ST_DEFINEEDITTEXT,
154  ST_DEFINEBITS,
155  ST_DEFINEBITSJPEG2,
156  ST_DEFINEBITSJPEG3,
157  ST_DEFINEBITSLOSSLESS,
158  ST_DEFINEBITSLOSSLESS2,
159  ST_DEFINEMOVIE,
160  ST_DEFINESPRITE,
161  ST_DEFINEBUTTON,
162  ST_DEFINEBUTTON2,
163  ST_DEFINESOUND,
164  -1
165 };
166
167 // tags which may be used inside a sprite definition
168 static int swf_spritetagids[] =
169 {ST_SHOWFRAME,
170  ST_PLACEOBJECT,
171  ST_PLACEOBJECT2,
172  ST_REMOVEOBJECT,
173  ST_REMOVEOBJECT2, //?
174  ST_DOACTION,
175  ST_STARTSOUND,
176  ST_FRAMELABEL,
177  ST_SOUNDSTREAMHEAD,
178  ST_SOUNDSTREAMHEAD2,
179  ST_SOUNDSTREAMBLOCK,
180  ST_END,
181  -1
182 };
183
184 static int swf_pseudodefiningtagids[] = 
185 {
186  ST_DEFINEFONTINFO,
187  ST_DEFINEBUTTONCXFORM,
188  ST_DEFINEBUTTONSOUND,
189  ST_NAMECHARACTER,
190  -1
191 };
192
193 U8 swf_isAllowedSpriteTag(TAG * tag)
194 {
195     int id = tag->id;
196     int t=0;
197     while(swf_spritetagids[t]>=0)
198     {
199         if(swf_spritetagids[t] == id) 
200             return 1;
201         t++;
202     }
203     return 0; 
204 }
205
206 U8 swf_isDefiningTag(TAG * tag)
207 {
208     int id = tag->id;
209     int t=0;
210     while(swf_definingtagids[t]>=0)
211     {
212         if(swf_definingtagids[t] == id) 
213             return 1;
214         t++;
215     }
216     return 0; 
217 }
218
219 U8 swf_isPseudoDefiningTag(TAG * tag)
220 {
221     int id = tag->id;
222     int t=0;
223     while(swf_pseudodefiningtagids[t]>=0)
224     {
225         if(swf_pseudodefiningtagids[t] == id) 
226             return 1;
227         t++;
228     }
229     return 0; 
230 }
231
232 U16 swf_GetDepth(TAG * t)
233 // up to SWF 4.0
234
235   U16 depth = 0;
236   U32 oldTagPos;
237   oldTagPos = swf_GetTagPos(t);
238   swf_SetTagPos(t,0);
239
240   switch (swf_GetTagID(t))
241   { case ST_PLACEOBJECT:
242     case ST_REMOVEOBJECT:
243       swf_GetU16(t); //id
244       depth = swf_GetU16(t);
245       break;
246     case ST_REMOVEOBJECT2:
247       depth = swf_GetU16(t);
248       break;
249     case ST_PLACEOBJECT2:
250     { U8 flags = swf_GetU8(t);
251       depth = swf_GetU16(t);
252     } break;
253   }
254   swf_SetTagPos(t,oldTagPos);
255   return depth;
256 }
257
258 char* swf_GetName(TAG * t)
259 {
260     char* name = 0;
261     U32 oldTagPos;
262     MATRIX m;
263     CXFORM c;
264     oldTagPos = swf_GetTagPos(t);
265     swf_SetTagPos(t,0);
266     switch(swf_GetTagID(t))
267     {
268         case ST_FRAMELABEL:
269             name = &t->data[swf_GetTagPos(t)];
270         break;
271         case ST_PLACEOBJECT2: {   
272             U8 flags = swf_GetU8(t);
273             swf_GetU16(t); //depth;
274             if(flags&PF_CHAR) 
275               swf_GetU16(t); //id
276             if(flags&PF_MATRIX)
277               swf_GetMatrix(t, &m);
278             if(flags&PF_CXFORM)
279               swf_GetCXForm(t, &c, 1);
280             if(flags&PF_RATIO)
281               swf_GetU16(t);
282             if(flags&PF_NAME) {
283               swf_ResetReadBits(t);
284               name = &t->data[swf_GetTagPos(t)];
285             }
286         }
287         break;
288     }
289     swf_SetTagPos(t,oldTagPos);
290     return name;
291 }
292
293 static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num)
294 {
295     U16 count;
296     int t;
297     count = swf_GetU8(tag);
298     if(count == 0xff && num>1) // defineshape2,3 only
299         count = swf_GetU16(tag);
300
301     for(t=0;t<count;t++)
302     {
303         int type;
304         U8*pos;
305         swf_ResetReadBits(tag);
306         type = swf_GetU8(tag); //type
307         if(type == 0) {
308             if(num == 3)
309                 swf_GetRGBA(tag, NULL);
310             else 
311                 swf_GetRGB(tag, NULL);
312         }
313         else if(type == 0x10 || type == 0x12)
314         {
315             swf_ResetReadBits(tag);
316             swf_GetMatrix(tag, NULL);
317             swf_ResetReadBits(tag);
318             swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
319         }
320         else if(type == 0x40 || type == 0x41)
321         {
322             swf_ResetReadBits(tag);
323             // we made it.
324             if(tag->data[tag->pos] != 0xff ||
325                tag->data[tag->pos+1] != 0xff)
326             (callback)(tag, tag->pos, callback_data);
327
328             swf_GetU16(tag);
329             swf_ResetReadBits(tag);
330             swf_GetMatrix(tag, NULL);
331         }
332         else {
333             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
334         }
335     }
336     swf_ResetReadBits(tag);
337     count = swf_GetU8(tag); // line style array
338     if(count == 0xff)
339         count = swf_GetU16(tag);
340     for(t=0;t<count;t++) 
341     {
342         swf_GetU16(tag);
343         if(num == 3)
344             swf_GetRGBA(tag, NULL);
345         else
346             swf_GetRGB(tag, NULL);
347     }
348 }
349
350 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
351 {
352     int num = 1;
353     swf_ResetReadBits(tag);
354     tag->pos = 0;
355     switch(tag->id)
356     {
357         case ST_DEFINEBUTTONCXFORM: {
358             int t;
359             callback(tag, tag->pos + base, callback_data);
360             for(t=0;t<4;t++) {
361                 int flags;
362                 callback(tag, tag->pos + base, callback_data);
363                 swf_GetU16(tag); //sound id
364                 flags = swf_GetU8(tag);
365                 if(flags&1)
366                     swf_GetU32(tag); // in point
367                 if(flags&2)
368                     swf_GetU32(tag); // out points
369                 if(flags&4)
370                     swf_GetU16(tag); // loop count
371                 if(flags&8)
372                 {
373                     int npoints = swf_GetU8(tag);
374                     int s;
375                     for(s=0;s<npoints;s++)
376                     {
377                         swf_GetU32(tag);
378                         swf_GetU16(tag);
379                         swf_GetU16(tag);
380                     }
381                 }
382             }
383         } break;
384         case ST_DEFINEBUTTONSOUND:
385             callback(tag, tag->pos + base, callback_data); //button id
386         break;
387         case ST_PLACEOBJECT:
388             callback(tag, tag->pos + base, callback_data);
389         break;
390         case ST_PLACEOBJECT2:
391             // only if placeflaghascharacter
392             if(!(tag->data[0]&2))
393                 break;
394             callback(tag, 3 + base, callback_data);
395         break;
396         case ST_REMOVEOBJECT:
397             callback(tag, tag->pos + base, callback_data);
398         break;
399         case ST_STARTSOUND:
400             callback(tag, tag->pos + base, callback_data);
401         break;
402         case ST_DEFINESPRITE: {
403             if(tag->len <= 4)
404                 break; // sprite is expanded
405
406             swf_GetU16(tag); // id
407             swf_GetU16(tag); // framenum
408
409             while(1) {
410                 U16 flags = swf_GetU16(tag);
411                 U32 len;
412                 U16 id = flags>>6;
413                 TAG *tag2 = swf_InsertTag(NULL, id);
414                 len = flags&0x3f;
415                 if(len == 63)
416                     len = swf_GetU32(tag);
417                 if(id == ST_END)
418                     break;
419                 tag2->len = tag2->memsize = len;
420                 tag2->data = malloc(len);
421                 memcpy(tag2->data, &tag->data[tag->pos], len);
422                 /* I never saw recursive sprites, but they are (theoretically) 
423                    possible, so better add base here again */
424                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
425                 swf_DeleteTag(tag2);
426                 swf_GetBlock(tag, NULL, len);
427             }
428         } 
429         break;
430         case ST_DEFINEBUTTON2: // has some font ids in the button records
431             num++; 
432         //fallthrough
433         case ST_DEFINEBUTTON: {
434             swf_GetU16(tag); //button id
435             if(num>1)
436             { 
437                 int offset;
438                 swf_GetU8(tag); //flag
439                 offset = swf_GetU16(tag); //offset
440             }
441             while(1)
442             {
443                 U16 charid;
444                 if(!swf_GetU8(tag)) //flags
445                     break; 
446                 callback(tag, tag->pos + base, callback_data);
447                 swf_GetU16(tag); //char
448                 swf_GetU16(tag); //layer
449                 swf_ResetReadBits(tag);
450                 swf_GetMatrix(tag, NULL);
451                 if(num>1) {
452                   swf_ResetReadBits(tag);
453                   swf_GetCXForm(tag, NULL, 1);
454                 }
455             }
456             // ...
457         }
458         break;
459         case ST_DEFINEEDITTEXT:  {
460             U8 flags1,flags2;
461             swf_GetU16(tag); //id
462             swf_GetRect(tag, NULL); //bounding box
463             swf_ResetReadBits(tag);
464             flags1 = swf_GetU8(tag);
465             flags2 = swf_GetU8(tag);
466             if(flags1 & 1)
467                 callback(tag, tag->pos + base, callback_data);
468         }
469         break;
470         case ST_DEFINETEXT2:
471             num ++;
472         case ST_DEFINETEXT: { 
473             int glyphbits, advancebits;
474             int id;
475             id = swf_GetU16(tag); //id
476             swf_GetRect(tag, NULL); //bounding box
477             swf_ResetReadBits(tag);
478             swf_GetMatrix(tag, NULL); //matrix
479             swf_ResetReadBits(tag);
480             glyphbits = swf_GetU8(tag); //glyphbits
481             advancebits = swf_GetU8(tag); //advancebits
482             while(1) {
483                 U16 flags;
484                 swf_ResetReadBits(tag);
485                 flags = swf_GetBits(tag, 8);
486                 if(!flags) break;
487                 if(flags & 128) // text style record
488                 {
489                     swf_ResetReadBits(tag);
490                     if(flags & 8) { // hasfont
491                         callback(tag, tag->pos + base, callback_data);
492                         id = swf_GetU16(tag);
493                     }
494                     if(flags & 4) { // hascolor
495                         if(num==1) swf_GetRGB(tag, NULL);
496                         else       swf_GetRGBA(tag, NULL);
497                     }
498                     if(flags & 2) { //has x offset
499                         swf_ResetReadBits(tag);
500                         swf_GetU16(tag);
501                     }
502                     if(flags & 1) { //has y offset
503                         swf_ResetReadBits(tag);
504                         swf_GetU16(tag);
505                     }
506                     if(flags & 8) { //has height
507                         swf_ResetReadBits(tag);
508                         swf_GetU16(tag);
509                     }
510                 } else { // glyph record
511                     int t;
512                     swf_ResetReadBits(tag);
513                     for(t=0;t<flags;t++) {
514                         swf_GetBits(tag, glyphbits);
515                         swf_GetBits(tag, advancebits);
516                     }
517                 }
518             }
519             break;
520         }
521         case ST_DEFINEFONTINFO:
522             callback(tag, tag->pos + base, callback_data);
523         break;
524
525         case ST_DEFINESHAPE3: // these thingies might have bitmap ids in their fillstyles
526         num++; //fallthrough
527         case ST_DEFINESHAPE2:
528         num++; //fallthrough
529         case ST_DEFINESHAPE: {
530             int fillbits;
531             int linebits;
532             int id; 
533             id = swf_GetU16(tag); // id;
534             swf_GetRect(tag, NULL); // bounds
535
536             enumerateUsedIDs_styles(tag, callback, callback_data, num);
537             fillbits = swf_GetBits(tag, 4);
538             linebits = swf_GetBits(tag, 4);
539             swf_ResetReadBits(tag);
540             while(1) {
541                 int flags;
542                 flags = swf_GetBits(tag, 1);
543                 if(!flags) { //style change
544                     flags = swf_GetBits(tag, 5);
545                     if(!flags)
546                         break;
547                     if(flags&1) { //move
548                         int n = swf_GetBits(tag, 5); 
549                         swf_GetBits(tag, n); //x
550                         swf_GetBits(tag, n); //y
551                     }
552                     if(flags&2) { //fill0
553                         swf_GetBits(tag, fillbits); 
554                     }
555                     if(flags&4) { //fill1
556                         swf_GetBits(tag, fillbits); 
557                     }
558                     if(flags&8) { //linestyle
559                         swf_GetBits(tag, linebits); 
560                     }
561                     if(flags&16) {
562                         enumerateUsedIDs_styles(tag, callback, callback_data, num);
563                         fillbits = swf_GetBits(tag, 4);
564                         linebits = swf_GetBits(tag, 4);
565                     }
566                 } else {
567                     flags = swf_GetBits(tag, 1);
568                     if(flags) { //straight edge
569                         int n = swf_GetBits(tag, 4) + 2;
570                         if(swf_GetBits(tag, 1)) { //line flag
571                             swf_GetBits(tag, n); //delta x
572                             swf_GetBits(tag, n); //delta y
573                         } else {
574                             int v=swf_GetBits(tag, 1);
575                             swf_GetBits(tag, n); //vert/horz
576                         }
577                     } else { //curved edge
578                         int n = swf_GetBits(tag, 4) + 2;
579                         swf_GetBits(tag, n);
580                         swf_GetBits(tag, n);
581                         swf_GetBits(tag, n);
582                         swf_GetBits(tag, n);
583                     }
584                 }
585             }
586         }
587         break;
588         default:
589         break;
590     }
591 }
592
593 void callbackCount(TAG * t,int pos, void*ptr)
594 {
595     (*(int*)ptr)++;
596 }
597
598 void callbackFillin(TAG * t,int pos, void*ptr)
599 {
600     **(int**)ptr = pos;
601     (*(int**)ptr)++;
602 }
603
604 int swf_GetNumUsedIDs(TAG * t)
605 {
606     int num = 0;
607     enumerateUsedIDs(t, 0, callbackCount, &num);
608     return num;
609 }
610
611 void swf_GetUsedIDs(TAG * t, int * positions)
612 {
613     int * ptr = positions;
614     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
615 }
616
617 void swf_Relocate (SWF*swf, char*bitmap)
618 {
619     TAG*tag;
620     int slaveids[65536];
621     memset(slaveids, -1, sizeof(slaveids));
622     tag = swf->firstTag;
623     while(tag)
624     {
625         int num; 
626         int *ptr;
627         int t;
628
629         if(swf_isDefiningTag(tag))
630         {
631             int newid;
632             int id;
633             
634             id = swf_GetDefineID(tag); //own id
635
636             if(!bitmap[id]) { //free
637                 newid = id;
638             }
639             else {
640                 newid = 0;
641                 for (t=1;t<65536;t++)
642                 {
643                     if(!bitmap[t])
644                     {
645                         newid = t;
646                         break;
647                     }
648                 }
649             }
650             bitmap[newid] = 1;
651             slaveids[id] = newid;
652
653             swf_SetDefineID(tag, newid);
654         } 
655         
656         num = swf_GetNumUsedIDs(tag);
657         ptr = malloc(sizeof(int)*num);
658         swf_GetUsedIDs(tag, ptr);
659
660         for(t=0;t<num;t++) {
661             int id = GET16(&tag->data[ptr[t]]);
662             if(slaveids[id]<0) {
663                 fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
664                 return ;
665             }
666             id = slaveids[id];
667             PUT16(&tag->data[ptr[t]], id);
668         }
669         tag=tag->next;
670     }
671 }
672