set dpi to 72.
[swftools.git] / src / flash.c
1 /* flash.c
2    Various routines for reading and writing swf files and tags.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org> 
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include "flash.h"
11 #include "bitio.h"
12
13 void swf_init(struct reader_t*r, uchar*newdata, int newlength)
14 {
15     reader_init (r, &newdata[3], newlength - 3);
16 }
17
18 struct flash_header swf_read_header(struct reader_t*r)
19 {
20     struct flash_header head;
21     u16 rate;
22     u16 count;
23     char version;
24     int length;
25     u8* oldpos = reader_getinputpos(r);
26
27     reader_input1(r,&version);
28     head.version = version;
29     reader_input4(r,&length);
30     head.length = length;
31     
32     reader_resetbits(r);
33     head.boundingBox = readRECT(r);
34     reader_input2(r,&rate);
35     head.rate = rate;
36     reader_input2(r,&count);
37     head.count = count;
38
39     head.headerlength = reader_getinputpos(r) - oldpos;
40     head.headerdata = oldpos;
41
42     return head;
43 }
44
45 void swf_write_header(struct writer_t*w, struct flash_header*head)
46 {
47     writer_writeu8(w, head->version);
48     writer_writeu32(w, head->length);
49     
50     writer_writebits(w, 31, 5); 
51     writer_writebits(w, head->boundingBox.x1, 31);
52     writer_writebits(w, head->boundingBox.x2, 31);
53     writer_writebits(w, head->boundingBox.y1, 31);
54     writer_writebits(w, head->boundingBox.y2, 31);
55     writer_resetbits(w);
56     writer_writeu16(w, head->rate);
57     writer_writeu16(w, head->count);
58 }
59
60 struct RGB readRGB(struct reader_t*r)
61 {
62     struct RGB rgb;
63     reader_input1(r,&rgb.r);
64     reader_input1(r,&rgb.g);
65     reader_input1(r,&rgb.b);
66     return rgb;
67 }
68
69 struct RGBA readRGBA(struct reader_t*r)
70 {
71     struct RGBA rgba;
72     reader_input1(r,&rgba.r);
73     reader_input1(r,&rgba.g);
74     reader_input1(r,&rgba.b);
75     reader_input1(r,&rgba.a);
76     return rgba;
77 }
78
79 struct GRADIENT readGRADIENT(struct reader_t*r, int shape)
80 {
81     struct GRADIENT gradient;
82     int t;
83     gradient.num = reader_readu8(r);
84     for(t=0;t<gradient.num;t++)
85     {
86         gradient.ratios[t] = reader_readu8(r);
87         if(shape<3)
88             gradient.rgb[t] = readRGB(r);
89         else
90             gradient.rgba[t] = readRGBA(r);
91     }
92 }
93
94 struct RECT readRECT(struct reader_t*reader)
95 {
96     u32 a;
97     struct RECT r;
98     s32 b;
99     reader_readbits(reader,&a,5);
100     reader_readsbits(reader,&b,a);
101     r.x1=b;
102     reader_readsbits(reader,&b,a);
103     r.x2=b;
104     reader_readsbits(reader,&b,a);
105     r.y1=b;
106     reader_readsbits(reader,&b,a);
107     r.y2=b;
108     return r;
109 }
110
111
112 void writeRECT(u8**pos, struct RECT*r)
113 {
114     struct writer_t w;
115     int t=0;
116     writer_init(&w, *pos, 256);
117     writer_writebits(&w, 31, 5); //FIXME:count
118     writer_writebits(&w, r->x1, 31);
119     writer_writebits(&w, r->x2, 31);
120     writer_writebits(&w, r->y1, 31);
121     writer_writebits(&w, r->y2, 31);
122     writer_resetbits(&w);
123     *pos = writer_getpos(&w);
124 }
125
126 struct CXFORM readCXFORM(struct reader_t*r, char alpha)
127 {
128     struct CXFORM c;
129     int bits;
130     c.hasadd=reader_readbit(r);
131     c.hasmult=reader_readbit(r);
132     bits=reader_getbits(r,4);
133     c.alpha = alpha;
134
135     if (c.hasmult)
136     {
137         c.rmult=reader_getsbits(r,bits)/65536.0;
138         c.gmult=reader_getsbits(r,bits)/65536.0;
139         c.bmult=reader_getsbits(r,bits)/65536.0;
140         if(c.alpha)
141             c.amult=reader_getsbits(r,bits)/65536.0;
142     }
143     if (c.hasadd)
144     {
145         c.radd=reader_getsbits(r,bits)/65536.0;
146         c.gadd=reader_getsbits(r,bits)/65536.0;
147         c.badd=reader_getsbits(r,bits)/65536.0;
148         if(c.alpha)
149             c.aadd=reader_getsbits(r,bits)/65536.0;
150     }
151     return c;
152 }
153
154 void CXFORM_write(struct CXFORM *obj, struct writer_t*w)
155 {
156     int bits = 15;
157     writer_writebit(w,obj->hasadd);
158     writer_writebit(w,obj->hasmult);
159     writer_writebits(w, bits, 4);
160     if (obj->hasmult)
161     {
162         writer_writebits(w, obj->rmult, bits);
163         writer_writebits(w, obj->gmult, bits);
164         writer_writebits(w, obj->bmult, bits);
165         if(obj->alpha)
166             writer_writebits(w, obj->amult, 4);
167     }
168     if (obj->hasadd)
169     {
170         writer_writebits(w, obj->radd, bits);
171         writer_writebits(w, obj->gadd, bits);
172         writer_writebits(w, obj->badd, bits);
173         if(obj->alpha)
174             writer_writebits(w, obj->aadd, 4);
175     }
176 }
177
178 unsigned char* readSTRING(struct reader_t*r)
179 {
180     unsigned char*now = reader_getinputpos(r);
181     char a;
182     do
183     {
184         reader_input1(r,&a);
185     }
186     while(a);
187     return now;
188 }
189 void MATRIX_init(struct MATRIX*m)
190 {
191     m->hasrotate = 0;
192     m->hasscale = 0;
193     m->b[0] = 0; 
194     m->b[1] = 0; 
195     m->a[0][0] = 1;
196     m->a[1][1] = 1;
197     m->a[1][0] = 0;
198     m->a[0][1] = 0;
199 }
200 struct MATRIX readMATRIX(struct reader_t*r)
201 {
202     struct MATRIX m;
203     u8 hasrotate;
204     u8 translatebits;
205     u32 translatex;
206     u32 translatey;
207
208     m.a[0][0] = m.a[1][1] = 1;
209     m.a[0][1] = m.a[1][0] = 0;
210     m.hasscale=reader_readbit(r);
211     if(m.hasscale)
212     {
213         u8 scalebits=reader_getbits(r,5);
214         s32 scalex=reader_getsbits(r,scalebits);
215         s32 scaley=reader_getsbits(r,scalebits);
216         m.a[0][0]=scalex/65536.0;
217         m.a[1][1]=scaley/65536.0;
218     }
219     m.hasrotate=reader_readbit(r);
220     if(m.hasrotate)
221     {
222         u8 rotbits=reader_getbits(r,5);
223         s32 rotateskew0=reader_getsbits(r,rotbits);
224         s32 rotateskew1=reader_getsbits(r,rotbits);
225         m.a[0][1]=rotateskew0/65536.0;
226         m.a[1][0]=rotateskew1/65536.0;
227     }
228
229     translatebits=reader_getbits(r,5);
230     translatex=reader_getsbits(r,translatebits);
231     translatey=reader_getsbits(r,translatebits);
232     m.b[0]=translatex;
233     m.b[1]=translatey;
234
235     return m;
236 }
237
238 void MATRIX_write(struct MATRIX * m , struct writer_t*w)
239 {
240     writer_writebit (w, m->hasscale);
241     if(m->hasscale)
242     {
243         int bits = 31;
244         writer_writebits(w, bits, 5);
245         writer_writebits(w, (u32)(m->a[0][0]*65536.0), bits);
246         writer_writebits(w, (u32)(m->a[1][1]*65536.0), bits);
247     }
248     writer_writebit (w, m->hasrotate);
249     if(m->hasrotate)
250     {
251         int bits = 31;
252         writer_writebits(w, bits, 5);
253         writer_writebits(w, (u32)(m->a[0][1]*65536.0), bits);
254         writer_writebits(w, (u32)(m->a[1][0]*65536.0), bits);
255     }
256
257     //translate
258     {
259         int bits = 31;
260         writer_writebits(w, bits, 5);
261         writer_writebits(w, (u32)(m->b[0]), bits);
262         writer_writebits(w, (u32)(m->b[1]), bits);
263     }
264 }
265
266
267 int swf_read_tag(struct reader_t*r, struct swf_tag* swftag)
268 {
269     u8*pos2,*pos = reader_getinputpos(r);
270     u16 tag;
271     u32 taglength;
272     u32 tagid;
273     int t;
274
275     reader_input2(r, &tag);
276
277     taglength = tag & 0x3f;
278     if (taglength == 0x3f)
279     {
280         reader_input4(r, &taglength);
281     }
282
283     swftag->id=tag>>6;
284     swftag->length = taglength;
285     swftag->data = reader_getinputpos(r);
286     reader_skip(r,taglength);
287     pos2 = reader_getinputpos(r);
288     swftag->fulllength = pos2 - pos;
289     swftag->fulldata = pos;
290     return 1;
291 }
292
293 int swf_count_tags(struct reader_t*r)
294 {
295     u8*pos = reader_getinputpos(r);
296     int t=0;
297     struct swf_tag tag;
298
299     while(1)
300     {
301         swf_read_tag(r,&tag);
302         t++;
303         if (tag.id == 0)
304             break;
305     }
306     
307     reader_setinputpos(r,pos);
308     return t;
309 }
310
311 void placeobject_init (struct PlaceObject*obj, struct swf_tag*tag)
312 {
313     struct reader_t r;
314     reader_init (&r, tag->data, tag->length);
315     obj -> id = reader_readu16(&r);
316     obj -> depth = reader_readu16(&r);
317     obj -> matrix = readMATRIX(&r);
318     obj -> hascxform = (reader_getinputpos(&r) < &tag->data[tag->length]);
319     if(obj -> hascxform)
320         obj -> cxform = readCXFORM(&r, 0);
321 }
322
323 void placeobject_write (struct PlaceObject*obj, struct writer_t*w)
324 {
325     u16 taghead = 0x3f | TAGID_PLACEOBJECT<<6;
326     u8*pos;
327     u8*lenpos;
328     writer_resetbits(w);
329     writer_write(w, &taghead, 2);
330     lenpos = writer_getpos(w);
331
332     writer_write(w, &taghead, 2);
333     writer_write(w, &taghead, 2);
334
335     pos = writer_getpos(w);
336
337     writer_write(w, &obj->id, 2);
338     writer_write(w, &obj->depth, 2);
339     MATRIX_write(&obj->matrix, w);
340
341     if(obj->hascxform)
342     {
343         CXFORM_write(&obj->cxform, w);
344     }
345     writer_resetbits(w);
346     
347     *(u32*)lenpos = SWAP32((u8*)writer_getpos(w) - pos);
348 }
349
350 void placeobject2_init (struct PlaceObject2*obj,struct swf_tag*tag)
351 {    
352     struct reader_t r;
353     u8 b;
354     reader_init (&r, tag->data, tag->length);
355     b=reader_readu8(&r);
356     obj->reserved=         (b>>7)&1;
357     obj->hasclipactions=   (b>>6)&1;
358     obj->hasname=          (b>>5)&1;
359     obj->hasratio=         (b>>4)&1;
360     obj->hascolortransform=(b>>3)&1;
361     obj->hasmatrix=        (b>>2)&1;
362     obj->hascharacter=     (b>>1)&1;
363     obj->hasmove=          (b>>0)&1;
364
365     obj->depth = reader_readu16(&r);
366     obj->id = -1;
367     if(obj->hascharacter) {
368         obj->id = reader_readu16(&r);
369     }
370     if(obj->hasmatrix) { 
371         obj->matrix = readMATRIX(&r);
372     } 
373     if(obj->hascolortransform) {
374         obj->cxform = readCXFORM(&r,0);
375     }
376     if(obj->hasratio) {
377         obj->ratio=reader_readu16(&r);
378     }
379     obj->name = 0;
380     if(obj->hasname) {
381         obj->name=readSTRING(&r);
382     }
383     if(obj->hasclipactions) {
384         obj->clipactions=reader_readu16(&r);
385     }
386 }
387
388 void placeobject2_write (struct PlaceObject2*obj, struct writer_t*w)
389 {
390     u8 flags = obj->reserved<<7 | obj->hasclipactions<<6 | obj->hasname<<5 | obj->hasratio<<4 |
391                obj->hascolortransform<<3 | obj->hasmatrix<<2 | obj->hascharacter<<1 | obj->hasmove;
392     u16 taghead = SWAP16(0x3f | TAGID_PLACEOBJECT2<<6);
393     u8*pos;
394     u8*lenpos;
395     writer_resetbits(w);
396     writer_write(w, &taghead, 2);
397     lenpos = writer_getpos(w);
398     writer_write(w, &taghead, 2);
399     writer_write(w, &taghead, 2);
400
401     pos = writer_getpos(w);
402
403     writer_writeu8(w, flags);
404     writer_writeu16(w, obj->depth);
405     if(obj->hascharacter)
406         writer_writeu16(w, obj->id);
407     if(obj->hasmatrix)
408         MATRIX_write(&obj->matrix, w);
409     if(obj->hascolortransform)
410         CXFORM_write(&obj->cxform, w);
411     if(obj->hasratio)
412         writer_writeu16(w, obj->ratio);
413     writer_resetbits(w);
414     if(obj->hasname)
415         writer_write(w, obj->name, strlen(obj->name) + 1);
416     if(obj->hasclipactions)
417         writer_writeu16(w, obj->clipactions);
418     writer_resetbits(w);
419     *(u32*)lenpos = SWAP32((u8*)writer_getpos(w) - pos);
420 }
421
422 void read_swf(struct swffile*swf, uchar*data, int length)
423 {
424     int pos;
425     struct flash_header head;
426     int tagnum;
427     struct reader_t r;
428     swf_init(&r, data, length);
429     head = swf_read_header(&r);
430     logf("<debug> [HEADER] the version is %d", head.version);
431     logf("<debug> [HEADER] the length is %d", head.length);
432     logf("<debug> [HEADER] the boundingBox is %d:%d:%d:%d", 
433             head.boundingBox.x1,head.boundingBox.y1,
434             head.boundingBox.x2,head.boundingBox.y2);
435     logf("<debug> [HEADER] the rate (frames/second) is %d", head.rate);
436     logf("<debug> [HEADER] the count (frame number) is %d", head.count);
437
438     tagnum = swf_count_tags(&r);
439     swf->tags = (struct swf_tag*)malloc(sizeof(struct swf_tag)*tagnum);
440     
441     logf("<debug> [HEADER] the file consists of %d tags", tagnum);
442
443     pos = 0;
444     while(1)
445     {
446         struct swf_tag tag;
447         swf_read_tag(&r, &tag);
448         logf("<debug> read tag %02x (%d bytes)", tag.id, tag.length);
449         swf->tags[pos] = tag;
450         pos++;
451         if(tag.id == TAGID_END)
452             break;
453     }
454     swf->tagnum = tagnum;
455     swf->header = head;
456 }
457
458 int definingtagids[] =
459 {TAGID_DEFINESHAPE,
460  TAGID_DEFINESHAPE2,
461  TAGID_DEFINESHAPE3,
462  TAGID_DEFINEMORPHSHAPE,
463  TAGID_DEFINEFONT,
464  TAGID_DEFINEFONT2,
465  TAGID_DEFINETEXT,
466  TAGID_DEFINETEXT2,
467  TAGID_DEFINEEDITTEXT,
468  TAGID_DEFINEBITS,
469  TAGID_DEFINEBITSJPEG2,
470  TAGID_DEFINEBITSJPEG3,
471  TAGID_DEFINEBITSLOSSLESS,
472  TAGID_DEFINEBITSLOSSLESS2,
473  TAGID_DEFINEMOVIE,
474  TAGID_DEFINESPRITE,
475  TAGID_DEFINEBUTTON,
476  TAGID_DEFINEBUTTON2,
477  TAGID_DEFINESOUND,
478  -1
479 };
480
481 // tags which may be used inside a sprite definition
482 int spritetagids[] =
483 {TAGID_SHOWFRAME,
484  TAGID_PLACEOBJECT,
485  TAGID_PLACEOBJECT2,
486  TAGID_REMOVEOBJECT,
487  TAGID_REMOVEOBJECT2, //?
488  TAGID_DOACTION,
489  TAGID_STARTSOUND,
490  TAGID_FRAMELABEL,
491  TAGID_SOUNDSTREAMHEAD,
492  TAGID_SOUNDSTREAMHEAD2,
493  TAGID_SOUNDSTREAMBLOCK,
494  TAGID_END,
495  -1
496 };
497
498 int getidfromtag(struct swf_tag* tag)
499 {
500     int num = 1;
501     switch(tag->id) {
502         case TAGID_PLACEOBJECT2:
503             num++;
504         case TAGID_PLACEOBJECT: {
505            struct reader_t r;
506            reader_init (&r, tag->data, tag->length);
507            if(num>=2) {
508                 char b = reader_readu8(&r);
509                 if(!(b&2))
510                     return -1;
511            }
512            reader_readu16(&r);
513            return reader_readu16(&r);
514         }
515         break;
516         case TAGID_REMOVEOBJECT:
517            return *(u16*)tag->data;
518         break;
519         case TAGID_REMOVEOBJECT2:
520            return -1;
521         break;
522     }
523
524     return *(u16*)tag->data;
525 }
526
527 void setidintag(struct swf_tag* tag, int id)
528 {
529     *(u16*)tag->data = SWAP16(id);
530 }
531
532 char is_sprite_tag (int id)
533 {
534     int t=0;
535     while(spritetagids[t]>=0)
536     {
537         if(spritetagids[t] == id) 
538             return 1;
539         t++;
540     }
541     return 0; 
542 }
543
544 char is_defining_tag (int id)
545 {
546     int t=0;
547     while(definingtagids[t]>=0)
548     {
549         if(definingtagids[t] == id) 
550             return 1;
551         t++;
552     }
553     return 0; 
554 }
555
556 struct swf_tag* duptag(struct swf_tag*tag)
557 {
558     struct swf_tag* newtag = (struct swf_tag*)malloc(sizeof(struct swf_tag));
559     newtag->id = tag->id;
560     newtag->fulldata = (u8*)malloc(tag->fulllength);
561     newtag->fulllength = tag->fulllength;
562     newtag->data = newtag->fulldata + (tag->data - tag->fulldata);
563     newtag->length = tag->length;
564     memcpy(newtag->fulldata, tag->fulldata, tag->fulllength);
565     return newtag;
566 }
567