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