added functions for extracting positions of referred IDs
[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 U16 swf_GetDefineID(TAG * t)
69 // up to SWF 4.0
70 { U32 oldTagPos;
71   U16 id = 0;
72
73   oldTagPos = swf_GetTagPos(t);
74   swf_SetTagPos(t,0);
75
76   switch (swf_GetTagID(t))
77   { case ST_DEFINESHAPE:
78     case ST_DEFINESHAPE2:
79     case ST_DEFINESHAPE3:
80     case ST_DEFINEMORPHSHAPE:
81     case ST_DEFINEEDITTEXT:
82     case ST_DEFINEBITS:
83     case ST_DEFINEBITSJPEG2:
84     case ST_DEFINEBITSJPEG3:
85     case ST_DEFINEBITSLOSSLESS:
86     case ST_DEFINEBITSLOSSLESS2:
87     case ST_DEFINEBUTTON:
88     case ST_DEFINEBUTTON2:
89     case ST_DEFINEBUTTONCXFORM: //pseudodefine
90     case ST_DEFINEBUTTONSOUND: //pseudodefine
91     case ST_DEFINEFONT:
92     case ST_DEFINEFONT2:
93     case ST_DEFINEFONTINFO: //pseudodefine
94     case ST_DEFINETEXT:
95     case ST_DEFINETEXT2:
96     case ST_DEFINESOUND:
97     case ST_DEFINESPRITE:
98     case ST_NAMECHARACTER: //pseudodefine
99       id = swf_GetU16(t);
100       break;
101   }
102
103   swf_SetTagPos(t,oldTagPos);
104
105   return id;
106 }
107
108 U16 swf_GetPlaceID(TAG * t)
109 // up to SWF 4.0
110 { U32 oldTagPos;
111   U16 id = 0;
112
113   oldTagPos = swf_GetTagPos(t);
114   swf_SetTagPos(t,0);
115
116   switch (swf_GetTagID(t))
117   { case ST_PLACEOBJECT:
118     case ST_REMOVEOBJECT:
119     case ST_STARTSOUND:
120       id = swf_GetU16(t);
121       break;
122
123     case ST_PLACEOBJECT2:
124     { U8 flags = swf_GetU8(t);
125       U16 d = swf_GetU16(t);
126       id = (flags&PF_CHAR)?swf_GetU16(t):id;
127     } break;
128
129   }
130
131   swf_SetTagPos(t,oldTagPos);
132
133   return id;
134 }
135
136 static int swf_definingtagids[] =
137 {ST_DEFINESHAPE,
138  ST_DEFINESHAPE2,
139  ST_DEFINESHAPE3,
140  ST_DEFINEMORPHSHAPE,
141  ST_DEFINEFONT,
142  ST_DEFINEFONT2,
143  ST_DEFINETEXT,
144  ST_DEFINETEXT2,
145  ST_DEFINEEDITTEXT,
146  ST_DEFINEBITS,
147  ST_DEFINEBITSJPEG2,
148  ST_DEFINEBITSJPEG3,
149  ST_DEFINEBITSLOSSLESS,
150  ST_DEFINEBITSLOSSLESS2,
151  ST_DEFINEMOVIE,
152  ST_DEFINESPRITE,
153  ST_DEFINEBUTTON,
154  ST_DEFINEBUTTON2,
155  ST_DEFINESOUND,
156  -1
157 };
158
159 // tags which may be used inside a sprite definition
160 static int swf_spritetagids[] =
161 {ST_SHOWFRAME,
162  ST_PLACEOBJECT,
163  ST_PLACEOBJECT2,
164  ST_REMOVEOBJECT,
165  ST_REMOVEOBJECT2, //?
166  ST_DOACTION,
167  ST_STARTSOUND,
168  ST_FRAMELABEL,
169  ST_SOUNDSTREAMHEAD,
170  ST_SOUNDSTREAMHEAD2,
171  ST_SOUNDSTREAMBLOCK,
172  ST_END,
173  -1
174 };
175
176 static int swf_pseudodefiningtagids[] = 
177 {
178  ST_DEFINEFONTINFO,
179  ST_DEFINEBUTTONCXFORM,
180  ST_DEFINEBUTTONSOUND,
181  ST_NAMECHARACTER,
182  -1
183 };
184
185 U8 swf_isAllowedSpriteTag(TAG * tag)
186 {
187     int id = tag->id;
188     int t=0;
189     while(swf_spritetagids[t]>=0)
190     {
191         if(swf_spritetagids[t] == id) 
192             return 1;
193         t++;
194     }
195     return 0; 
196 }
197
198 U8 swf_isDefiningTag(TAG * tag)
199 {
200     int id = tag->id;
201     int t=0;
202     while(swf_definingtagids[t]>=0)
203     {
204         if(swf_definingtagids[t] == id) 
205             return 1;
206         t++;
207     }
208     return 0; 
209 }
210
211 U8 swf_isPseudoDefiningTag(TAG * tag)
212 {
213     int id = tag->id;
214     int t=0;
215     while(swf_pseudodefiningtagids[t]>=0)
216     {
217         if(swf_pseudodefiningtagids[t] == id) 
218             return 1;
219         t++;
220     }
221     return 0; 
222 }
223
224 U16 swf_GetDepth(TAG * t)
225 // up to SWF 4.0
226
227   U16 depth = 0;
228   U32 oldTagPos;
229   oldTagPos = swf_GetTagPos(t);
230   swf_SetTagPos(t,0);
231
232   switch (swf_GetTagID(t))
233   { case ST_PLACEOBJECT:
234     case ST_REMOVEOBJECT:
235       swf_GetU16(t); //id
236       depth = swf_GetU16(t);
237       break;
238     case ST_REMOVEOBJECT2:
239       depth = swf_GetU16(t);
240       break;
241     case ST_PLACEOBJECT2:
242     { U8 flags = swf_GetU8(t);
243       depth = swf_GetU16(t);
244     } break;
245   }
246   swf_SetTagPos(t,oldTagPos);
247   return depth;
248 }
249
250 char* swf_GetName(TAG * t)
251 {
252     char* name = 0;
253     U32 oldTagPos;
254     MATRIX m;
255     CXFORM c;
256     oldTagPos = swf_GetTagPos(t);
257     swf_SetTagPos(t,0);
258     switch(swf_GetTagID(t))
259     {
260         case ST_FRAMELABEL:
261             name = &t->data[swf_GetTagPos(t)];
262         break;
263         case ST_PLACEOBJECT2: {   
264             U8 flags = swf_GetU8(t);
265             swf_GetU16(t); //depth;
266             if(flags&PF_CHAR) 
267               swf_GetU16(t); //id
268             if(flags&PF_MATRIX)
269               swf_GetMatrix(t, &m);
270             if(flags&PF_CXFORM)
271               swf_GetCXForm(t, &c, 1);
272             if(flags&PF_RATIO)
273               swf_GetU16(t);
274             if(flags&PF_NAME) {
275               swf_ResetReadBits(t);
276               name = &t->data[swf_GetTagPos(t)];
277             }
278         }
279         break;
280     }
281     swf_SetTagPos(t,oldTagPos);
282     return name;
283 }
284
285 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num)
286 {
287     U16 count;
288     int t;
289     count = swf_GetU8(tag);
290     if(count == 0xff && num>1) // defineshape2,3 only
291         count = swf_GetU16(tag);
292
293     for(t=0;t<count;t++)
294     {
295         int type;
296         U8*pos;
297         swf_ResetReadBits(tag);
298         type = swf_GetU8(tag); //type
299         if(type == 0) {
300             if(num == 3)
301                 swf_GetRGBA(tag, NULL);
302             else 
303                 swf_GetRGB(tag, NULL);
304         }
305         else if(type == 0x10 || type == 0x12)
306         {
307             swf_ResetReadBits(tag);
308             swf_GetMatrix(tag, NULL);
309             swf_ResetReadBits(tag);
310             swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
311         }
312         else if(type == 0x40 || type == 0x41)
313         {
314             swf_ResetReadBits(tag);
315             // we made it.
316             if(tag->data[tag->pos] != 0xff ||
317                tag->data[tag->pos+1] != 0xff)
318             (callback)(tag, tag->pos, callback_data);
319
320             swf_GetU16(tag);
321             swf_ResetReadBits(tag);
322             swf_GetMatrix(tag, NULL);
323         }
324         else {
325             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
326         }
327     }
328     swf_ResetReadBits(tag);
329     count = swf_GetU8(tag); // line style array
330     if(count == 0xff)
331         count = swf_GetU16(tag);
332     for(t=0;t<count;t++) 
333     {
334         swf_GetU16(tag);
335         if(num == 3)
336             swf_GetRGBA(tag, NULL);
337         else
338             swf_GetRGB(tag, NULL);
339     }
340 }
341
342 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
343 {
344     int num = 1;
345     swf_ResetReadBits(tag);
346     tag->pos = 0;
347     switch(tag->id)
348     {
349         case ST_DEFINEBUTTONCXFORM: {
350             int t;
351             callback(tag, tag->pos + base, callback_data);
352             for(t=0;t<4;t++) {
353                 int flags;
354                 callback(tag, tag->pos + base, callback_data);
355                 swf_GetU16(tag); //sound id
356                 flags = swf_GetU8(tag);
357                 if(flags&1)
358                     swf_GetU32(tag); // in point
359                 if(flags&2)
360                     swf_GetU32(tag); // out points
361                 if(flags&4)
362                     swf_GetU16(tag); // loop count
363                 if(flags&8)
364                 {
365                     int npoints = swf_GetU8(tag);
366                     int s;
367                     for(s=0;s<npoints;s++)
368                     {
369                         swf_GetU32(tag);
370                         swf_GetU16(tag);
371                         swf_GetU16(tag);
372                     }
373                 }
374             }
375         } break;
376         case ST_DEFINEBUTTONSOUND:
377             callback(tag, tag->pos + base, callback_data); //button id
378         break;
379         case ST_PLACEOBJECT:
380             callback(tag, tag->pos + base, callback_data);
381         break;
382         case ST_PLACEOBJECT2:
383             // only if placeflaghascharacter
384             if(!(tag->data[0]&2))
385                 break;
386             callback(tag, 3 + base, callback_data);
387         break;
388         case ST_REMOVEOBJECT:
389             callback(tag, tag->pos + base, callback_data);
390         break;
391         case ST_STARTSOUND:
392             callback(tag, tag->pos + base, callback_data);
393         break;
394         case ST_DEFINESPRITE: {
395             if(tag->len <= 4)
396                 break; // sprite is expanded
397
398             swf_GetU16(tag); // id
399             swf_GetU16(tag); // framenum
400
401             while(1) {
402                 U16 flags = swf_GetU16(tag);
403                 U32 len;
404                 U16 id = flags>>6;
405                 TAG *tag2 = swf_InsertTag(NULL, id);
406                 len = flags&0x3f;
407                 if(len == 63)
408                     len = swf_GetU32(tag);
409                 if(id == ST_END)
410                     break;
411                 tag2->len = tag2->memsize = len;
412                 tag2->data = malloc(len);
413                 memcpy(tag2->data, &tag->data[tag->pos], len);
414                 /* I never saw recursive sprites, but they are (theoretically) 
415                    possible, so better add base here again */
416                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
417                 swf_DeleteTag(tag2);
418                 swf_GetBlock(tag, NULL, len);
419             }
420         } 
421         break;
422         case ST_DEFINEBUTTON2: // has some font ids in the button records
423             num++; 
424         //fallthrough
425         case ST_DEFINEBUTTON: {
426             swf_GetU16(tag); //button id
427             if(num>1)
428             { 
429                 int offset;
430                 swf_GetU8(tag); //flag
431                 offset = swf_GetU16(tag); //offset
432             }
433             while(1)
434             {
435                 U16 charid;
436                 if(!swf_GetU8(tag)) //flags
437                     break; 
438                 callback(tag, tag->pos + base, callback_data);
439                 swf_GetU16(tag); //char
440                 swf_GetU16(tag); //layer
441                 swf_ResetReadBits(tag);
442                 swf_GetMatrix(tag, NULL);
443                 if(num>1) {
444                   swf_ResetReadBits(tag);
445                   swf_GetCXForm(tag, NULL, 1);
446                 }
447             }
448             // ...
449         }
450         break;
451         case ST_DEFINEEDITTEXT:  {
452             U8 flags1,flags2;
453             swf_GetU16(tag); //id
454             swf_GetRect(tag, NULL); //bounding box
455             swf_ResetReadBits(tag);
456             flags1 = swf_GetU8(tag);
457             flags2 = swf_GetU8(tag);
458             if(flags1 & 1)
459                 callback(tag, tag->pos + base, callback_data);
460         }
461         break;
462         case ST_DEFINETEXT2:
463             num ++;
464         case ST_DEFINETEXT: { 
465             int glyphbits, advancebits;
466             int id;
467             id = swf_GetU16(tag); //id
468             swf_GetRect(tag, NULL); //bounding box
469             swf_ResetReadBits(tag);
470             swf_GetMatrix(tag, NULL); //matrix
471             swf_ResetReadBits(tag);
472             glyphbits = swf_GetU8(tag); //glyphbits
473             advancebits = swf_GetU8(tag); //advancebits
474             while(1) {
475                 U16 flags;
476                 swf_ResetReadBits(tag);
477                 flags = swf_GetBits(tag, 8);
478                 if(!flags) break;
479                 if(flags & 128) // text style record
480                 {
481                     swf_ResetReadBits(tag);
482                     if(flags & 8) { // hasfont
483                         callback(tag, tag->pos + base, callback_data);
484                         id = swf_GetU16(tag);
485                     }
486                     if(flags & 4) { // hascolor
487                         if(num==1) swf_GetRGB(tag, NULL);
488                         else       swf_GetRGBA(tag, NULL);
489                     }
490                     if(flags & 2) { //has x offset
491                         swf_ResetReadBits(tag);
492                         swf_GetU16(tag);
493                     }
494                     if(flags & 1) { //has y offset
495                         swf_ResetReadBits(tag);
496                         swf_GetU16(tag);
497                     }
498                     if(flags & 8) { //has height
499                         swf_ResetReadBits(tag);
500                         swf_GetU16(tag);
501                     }
502                 } else { // glyph record
503                     int t;
504                     swf_ResetReadBits(tag);
505                     for(t=0;t<flags;t++) {
506                         swf_GetBits(tag, glyphbits);
507                         swf_GetBits(tag, advancebits);
508                     }
509                 }
510             }
511             break;
512         }
513         case ST_DEFINEFONTINFO:
514             callback(tag, tag->pos + base, callback_data);
515         break;
516
517         case ST_DEFINESHAPE3: // these thingies might have bitmap ids in their fillstyles
518         num++; //fallthrough
519         case ST_DEFINESHAPE2:
520         num++; //fallthrough
521         case ST_DEFINESHAPE: {
522             int fillbits;
523             int linebits;
524             int id; 
525             id = swf_GetU16(tag); // id;
526             swf_GetRect(tag, NULL); // bounds
527
528             enumerateUsedIDs_styles(tag, callback, callback_data, num);
529             fillbits = swf_GetBits(tag, 4);
530             linebits = swf_GetBits(tag, 4);
531             swf_ResetReadBits(tag);
532             while(1) {
533                 int flags;
534                 flags = swf_GetBits(tag, 1);
535                 if(!flags) { //style change
536                     flags = swf_GetBits(tag, 5);
537                     if(!flags)
538                         break;
539                     if(flags&1) { //move
540                         int n = swf_GetBits(tag, 5); 
541                         swf_GetBits(tag, n); //x
542                         swf_GetBits(tag, n); //y
543                     }
544                     if(flags&2) { //fill0
545                         swf_GetBits(tag, fillbits); 
546                     }
547                     if(flags&4) { //fill1
548                         swf_GetBits(tag, fillbits); 
549                     }
550                     if(flags&8) { //linestyle
551                         swf_GetBits(tag, linebits); 
552                     }
553                     if(flags&16) {
554                         enumerateUsedIDs_styles(tag, callback, callback_data, num);
555                         fillbits = swf_GetBits(tag, 4);
556                         linebits = swf_GetBits(tag, 4);
557                     }
558                 } else {
559                     flags = swf_GetBits(tag, 1);
560                     if(flags) { //straight edge
561                         int n = swf_GetBits(tag, 4) + 2;
562                         if(swf_GetBits(tag, 1)) { //line flag
563                             swf_GetBits(tag, n); //delta x
564                             swf_GetBits(tag, n); //delta y
565                         } else {
566                             int v=swf_GetBits(tag, 1);
567                             swf_GetBits(tag, n); //vert/horz
568                         }
569                     } else { //curved edge
570                         int n = swf_GetBits(tag, 4) + 2;
571                         swf_GetBits(tag, n);
572                         swf_GetBits(tag, n);
573                         swf_GetBits(tag, n);
574                         swf_GetBits(tag, n);
575                     }
576                 }
577             }
578         }
579         break;
580         default:
581         break;
582     }
583 }
584
585 void callbackCount(TAG * t,int pos, void*ptr)
586 {
587     (*(int*)ptr)++;
588 }
589
590 void callbackFillin(TAG * t,int pos, void*ptr)
591 {
592     **(int**)ptr = pos;
593     (*(int**)ptr)++;
594 }
595
596 int swf_GetNumUsedIDs(TAG * t)
597 {
598     int num = 0;
599     enumerateUsedIDs(t, 0, callbackCount, &num);
600     return num;
601 }
602
603 void swf_GetUsedIDs(TAG * t, int * positions)
604 {
605     int * ptr = positions;
606     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
607 }
608