version is now 0.2.1
[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(uchar*newdata, int newlength)
14 {
15     reader_init (&newdata[3], newlength - 3);
16 }
17
18 struct flash_header swf_read_header()
19 {
20     struct flash_header head;
21     u16 rate;
22     u16 count;
23     char version;
24     int length;
25     u8* oldpos = getinputpos();
26
27     input1(&version);
28     head.version = version;
29     input4(&length);
30     head.length = length;
31     
32     resetbits();
33     head.boundingBox = readRECT();
34     input2(&rate);
35     head.rate = rate;
36     input2(&count);
37     head.count = count;
38
39     head.headerlength = getinputpos() - 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()
61 {
62     struct RGB rgb;
63     input1(&rgb.r);
64     input1(&rgb.g);
65     input1(&rgb.b);
66     return rgb;
67 }
68
69 struct RGBA readRGBA()
70 {
71     struct RGBA rgba;
72     input1(&rgba.r);
73     input1(&rgba.g);
74     input1(&rgba.b);
75     input1(&rgba.a);
76     return rgba;
77 }
78
79 struct GRADIENT readGRADIENT(int shape)
80 {
81     struct GRADIENT gradient;
82     int t;
83     gradient.num = readu8();
84     for(t=0;t<gradient.num;t++)
85     {
86         gradient.ratios[t] = readu8();
87         if(shape<3)
88             gradient.rgb[t] = readRGB();
89         else
90             gradient.rgba[t] = readRGBA();
91     }
92 }
93
94 struct RECT readRECT()
95 {
96     u32 a;
97     struct RECT r;
98     s32 b;
99     readbits(&a,5);
100     readsbits(&b,a);
101     r.x1=b;
102     readsbits(&b,a);
103     r.x2=b;
104     readsbits(&b,a);
105     r.y1=b;
106     readsbits(&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(char alpha)
127 {
128     struct CXFORM c;
129     int bits;
130     c.hasadd=readbit();
131     c.hasmult=readbit();
132     bits=getbits(4);
133     c.alpha = alpha;
134
135     if (c.hasmult)
136     {
137         c.rmult=getsbits(bits)/65536.0;
138         c.gmult=getsbits(bits)/65536.0;
139         c.bmult=getsbits(bits)/65536.0;
140         if(c.alpha)
141             c.amult=getsbits(bits)/65536.0;
142     }
143     if (c.hasadd)
144     {
145         c.radd=getsbits(bits)/65536.0;
146         c.gadd=getsbits(bits)/65536.0;
147         c.badd=getsbits(bits)/65536.0;
148         if(c.alpha)
149             c.aadd=getsbits(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()
179 {
180     unsigned char*now = getinputpos();
181     char a;
182     do
183     {
184         input1(&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()
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=readbit();
211     if(m.hasscale)
212     {
213         u8 scalebits=getbits(5);
214         s32 scalex=getsbits(scalebits);
215         s32 scaley=getsbits(scalebits);
216         m.a[0][0]=scalex/65536.0;
217         m.a[1][1]=scaley/65536.0;
218     }
219     m.hasrotate=readbit();
220     if(m.hasrotate)
221     {
222         u8 rotbits=getbits(5);
223         s32 rotateskew0=getsbits(rotbits);
224         s32 rotateskew1=getsbits(rotbits);
225         m.a[0][1]=rotateskew0/65536.0;
226         m.a[1][0]=rotateskew1/65536.0;
227     }
228
229     translatebits=getbits(5);
230     translatex=getsbits(translatebits);
231     translatey=getsbits(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 swf_tag* swftag)
268 {
269     u8*pos2,*pos = getinputpos();
270     u16 tag;
271     u32 taglength;
272     u32 tagid;
273     int t;
274
275     input2(&tag);
276
277     taglength = tag & 0x3f;
278     if (taglength == 0x3f)
279     {
280         input4(&taglength);
281     }
282
283     swftag->id=tag>>6;
284     swftag->length = taglength;
285     swftag->data = getinputpos();
286     skip(taglength);
287     pos2 = getinputpos();
288     swftag->fulllength = pos2 - pos;
289     swftag->fulldata = pos;
290     return 1;
291 }
292
293 int swf_count_tags()
294 {
295     u8*pos = getinputpos();
296     int t=0;
297     struct swf_tag tag;
298
299     while(1)
300     {
301         swf_read_tag(&tag);
302         t++;
303         if (tag.id == 0)
304             break;
305     }
306     
307     setinputpos(pos);
308     return t;
309 }
310
311 void placeobject_init (struct PlaceObject*obj, struct swf_tag*tag)
312 {
313     reader_init (tag->data, tag->length);
314     obj -> id = readu16();
315     obj -> depth = readu16();
316     obj -> matrix = readMATRIX();
317     obj -> hascxform = (getinputpos() < &tag->data[tag->length]);
318     if(obj -> hascxform)
319         obj -> cxform = readCXFORM(0);
320 }
321
322 void placeobject_write (struct PlaceObject*obj, struct writer_t*w)
323 {
324     u16 taghead = 0x3f | TAGID_PLACEOBJECT<<6;
325     u8*pos;
326     u8*lenpos;
327     writer_resetbits(w);
328     writer_write(w, &taghead, 2);
329     lenpos = writer_getpos(w);
330
331     writer_write(w, &taghead, 2);
332     writer_write(w, &taghead, 2);
333
334     pos = writer_getpos(w);
335
336     writer_write(w, &obj->id, 2);
337     writer_write(w, &obj->depth, 2);
338     MATRIX_write(&obj->matrix, w);
339
340     if(obj->hascxform)
341     {
342         CXFORM_write(&obj->cxform, w);
343     }
344     writer_resetbits(w);
345     
346     *(u32*)lenpos = (u8*)writer_getpos(w) - pos;
347 }
348
349 void placeobject2_init (struct PlaceObject2*obj,struct swf_tag*tag)
350 {    
351     u8 b;
352     reader_init (tag->data, tag->length);
353     b=readu8();
354     obj->reserved=         (b>>7)&1;
355     obj->hasclipactions=   (b>>6)&1;
356     obj->hasname=          (b>>5)&1;
357     obj->hasratio=         (b>>4)&1;
358     obj->hascolortransform=(b>>3)&1;
359     obj->hasmatrix=        (b>>2)&1;
360     obj->hascharacter=     (b>>1)&1;
361     obj->hasmove=          (b>>0)&1;
362
363     obj->depth = readu16();
364     obj->id = -1;
365     if(obj->hascharacter) {
366         obj->id = readu16();
367     }
368     if(obj->hasmatrix) { 
369         obj->matrix = readMATRIX();
370     } 
371     if(obj->hascolortransform) {
372         obj->cxform = readCXFORM(0);
373     }
374     if(obj->hasratio) {
375         obj->ratio=readu16();
376     }
377     obj->name = 0;
378     if(obj->hasname) {
379         obj->name=readSTRING();
380     }
381     if(obj->hasclipactions) {
382         obj->clipactions=readu16();
383     }
384 }
385
386 void placeobject2_write (struct PlaceObject2*obj, struct writer_t*w)
387 {
388     u8 flags = obj->reserved<<7 | obj->hasclipactions<<6 | obj->hasname<<5 | obj->hasratio<<4 |
389                obj->hascolortransform<<3 | obj->hasmatrix<<2 | obj->hascharacter<<1 | obj->hasmove;
390     u16 taghead = 0x3f | TAGID_PLACEOBJECT2<<6;
391     u8*pos;
392     u8*lenpos;
393     writer_resetbits(w);
394     writer_write(w, &taghead, 2);
395     lenpos = writer_getpos(w);
396     writer_write(w, &taghead, 2);
397     writer_write(w, &taghead, 2);
398
399     pos = writer_getpos(w);
400
401     writer_writeu8(w, flags);
402     writer_writeu16(w, obj->depth);
403     if(obj->hascharacter)
404         writer_writeu16(w, obj->id);
405     if(obj->hasmatrix)
406         MATRIX_write(&obj->matrix, w);
407     if(obj->hascolortransform)
408         CXFORM_write(&obj->cxform, w);
409     if(obj->hasratio)
410         writer_writeu16(w, obj->ratio);
411     writer_resetbits(w);
412     if(obj->hasname)
413         writer_write(w, obj->name, strlen(obj->name) + 1);
414     if(obj->hasclipactions)
415         writer_writeu16(w, obj->clipactions);
416     writer_resetbits(w);
417     *(u32*)lenpos = (u8*)writer_getpos(w) - pos;
418 }
419
420 void read_swf(struct swffile*swf, uchar*data, int length)
421 {
422     int pos;
423     struct flash_header head;
424     int tagnum;
425     swf_init(data, length);
426     head = swf_read_header(data);
427     logf("<debug> [HEADER] the version is %d", head.version);
428     logf("<debug> [HEADER] the length is %d", head.length);
429     logf("<debug> [HEADER] the boundingBox is %d:%d:%d:%d", 
430             head.boundingBox.x1,head.boundingBox.y1,
431             head.boundingBox.x2,head.boundingBox.y2);
432     logf("<debug> [HEADER] the rate (frames/second) is %d", head.rate);
433     logf("<debug> [HEADER] the count (frame number) is %d", head.count);
434
435     tagnum = swf_count_tags();
436     swf->tags = (struct swf_tag*)malloc(sizeof(struct swf_tag)*tagnum);
437     
438     logf("<debug> [HEADER] the file consists of %d tags", tagnum);
439
440     pos = 0;
441     while(1)
442     {
443         struct swf_tag tag;
444         swf_read_tag(&tag);
445         logf("<debug> read tag %02x (%d bytes)", tag.id, tag.length);
446         swf->tags[pos] = tag;
447         pos++;
448         if(tag.id == TAGID_END)
449             break;
450     }
451     swf->tagnum = tagnum;
452     swf->header = head;
453 }
454
455 int definingtagids[] =
456 {TAGID_DEFINESHAPE,
457  TAGID_DEFINESHAPE2,
458  TAGID_DEFINESHAPE3,
459  TAGID_DEFINEMORPHSHAPE,
460  TAGID_DEFINEFONT,
461  TAGID_DEFINEFONT2,
462  TAGID_DEFINETEXT,
463  TAGID_DEFINETEXT2,
464  TAGID_DEFINEEDITTEXT,
465  TAGID_DEFINEBITS,
466  TAGID_DEFINEBITSJPEG2,
467  TAGID_DEFINEBITSJPEG3,
468  TAGID_DEFINEBITSLOSSLESS,
469  TAGID_DEFINEBITSLOSSLESS2,
470  TAGID_DEFINEMOVIE,
471  TAGID_DEFINESPRITE,
472  TAGID_DEFINEBUTTON,
473  TAGID_DEFINEBUTTON2,
474  TAGID_DEFINESOUND,
475  -1
476 };
477
478 // tags which may be used inside a sprite definition
479 int spritetagids[] =
480 {TAGID_SHOWFRAME,
481  TAGID_PLACEOBJECT,
482  TAGID_PLACEOBJECT2,
483  TAGID_REMOVEOBJECT,
484  TAGID_REMOVEOBJECT2, //?
485  TAGID_DOACTION,
486  TAGID_STARTSOUND,
487  TAGID_FRAMELABEL,
488  TAGID_SOUNDSTREAMHEAD,
489  TAGID_SOUNDSTREAMHEAD2,
490  TAGID_SOUNDSTREAMBLOCK,
491  TAGID_END,
492  -1
493 };
494
495 int getidfromtag(struct swf_tag* tag)
496 {
497     int num = 1;
498     switch(tag->id) {
499         case TAGID_PLACEOBJECT2:
500             num++;
501         case TAGID_PLACEOBJECT:
502            reader_init (tag->data, tag->length);
503            if(num>=2) {
504                 char b = readu8();
505                 if(!(b&2))
506                     return -1;
507            }
508            readu16();
509            return readu16();
510         break;
511         case TAGID_REMOVEOBJECT:
512            return *(u16*)tag->data;
513         break;
514         case TAGID_REMOVEOBJECT2:
515            return -1;
516         break;
517     }
518
519     return *(u16*)tag->data;
520 }
521
522 void setidintag(struct swf_tag* tag, int id)
523 {
524     *(u16*)tag->data = id;
525 }
526
527 char is_sprite_tag (int id)
528 {
529
530     int t=0;
531     while(spritetagids[t]>=0)
532     {
533         if(spritetagids[t] == id) 
534             return 1;
535         t++;
536     }
537     return 0; 
538 }
539
540 char is_defining_tag (int id)
541 {
542
543     int t=0;
544     while(definingtagids[t]>=0)
545     {
546         if(definingtagids[t] == id) 
547             return 1;
548         t++;
549     }
550     return 0; 
551 }
552
553 struct swf_tag* duptag(struct swf_tag*tag)
554 {
555     struct swf_tag* newtag = (struct swf_tag*)malloc(sizeof(struct swf_tag));
556     newtag->id = tag->id;
557     newtag->fulldata = (u8*)malloc(tag->fulllength);
558     newtag->fulllength = tag->fulllength;
559     newtag->data = newtag->fulldata + (tag->data - tag->fulldata);
560     newtag->length = tag->length;
561     memcpy(newtag->fulldata, tag->fulldata, tag->fulllength);
562     return newtag;
563 }
564