added ttf2pt1 files.
[swftools.git] / lib / rfxswf.c
1 /* rfxswf.c
2
3    Library for creating and reading SWF files or parts of it.
4    There's a module directory which provides some extended functionality.
5    Most modules are included at the bottom of this file.
6
7    Part of the swftools package.
8
9    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
10  
11    This file is distributed under the GPL, see file COPYING for details 
12
13 */
14
15 #include "rfxswf.h"
16
17 #ifdef HAVE_LIBJPEG
18 #ifdef HAVE_JPEGLIB_H
19 #include <jpeglib.h>
20 #define _JPEGLIB_INCLUDED_
21 #endif // HAVE_JPEGLIB_H
22 #endif // HAVE_LIBJPEG
23
24 #ifdef HAVE_LIBZ
25 #ifdef HAVE_ZLIB_H
26 #include <zlib.h>
27 #define _ZLIB_INCLUDED_
28 #endif // HAVE_ZLIB_H
29 #endif // HAVE_LIBZ
30
31 // Win32 support may be broken since it was only tested in an older version for Watcom C
32 #ifdef __NT__
33 #  include <io.h>
34 #  include <malloc.h>
35 #  include <string.h>
36 #  ifdef DEBUG_RFXSWF
37 #    include <stdio.h>
38 #  endif
39 #else
40 #endif
41
42 // internal constants
43
44 #define MALLOC_SIZE     128
45 #define INSERT_RFX_TAG
46
47 #define MEMSIZE(l) (((l/MALLOC_SIZE)+1)*MALLOC_SIZE)
48
49 // inline wrapper functions
50
51 TAG * swf_NextTag(TAG * t) { return t->next; }
52 TAG * swf_PrevTag(TAG * t) { return t->prev; }
53 int   swf_GetFrameNo(TAG * t)  { return t->frame; }
54 U16   swf_GetTagID(TAG * t)    { return t->id; }
55 U32   swf_GetDataSize(TAG * t) { return t->len; }
56 U8*   swf_GetDataSizePtr(TAG * t) { return &(t->data[t->len]); }
57 U32   swf_GetTagPos(TAG * t)   { return t->pos; }
58
59 // Basic Data Access Functions
60
61 #define swf_ResetReadBits(tag)   if (tag->readBit)  { tag->pos++; tag->readBit = 0; }
62 #define swf_ResetWriteBits(tag)  if (tag->writeBit) { tag->writeBit = 0; }
63
64 // for future purpose: avoid high level lib functions to change tagpos/bitpos
65
66 #define swf_SaveTagPos(tag)
67 #define swf_RestoreTagPos(tag)
68
69 void swf_SetTagPos(TAG * t,U32 pos)
70 { swf_ResetReadBits(t);
71   if (pos<=t->len) t->pos = pos;
72   #ifdef DEBUG_RFXSWF
73   else fprintf(stderr,"SetTagPos() out of bounds: TagID = %i\n",t->id);
74   #endif
75 }
76
77 U8 swf_GetU8(TAG * t)
78 { swf_ResetReadBits(t);
79   #ifdef DEBUG_RFXSWF
80     if (t->pos>=t->len) 
81     { fprintf(stderr,"GetU8() out of bounds: TagID = %i\n",t->id);
82       return 0;
83     }
84   #endif
85   return t->data[t->pos++];
86 }
87
88 U16 swf_GetU16(TAG * t)
89 { U16 res;
90   swf_ResetReadBits(t);
91   #ifdef DEBUG_RFXSWF
92     if (t->pos>(t->len-2)) 
93     { fprintf(stderr,"GetU16() out of bounds: TagID = %i\n",t->id);
94       return 0;
95     }
96   #endif
97   res = t->data[t->pos] | (t->data[t->pos+1]<<8);
98   t->pos+=2;
99   return res;
100 }
101
102 U32 swf_GetU32(TAG * t)
103 { U32 res;
104   swf_ResetReadBits(t);
105   #ifdef DEBUG_RFXSWF
106     if (t->pos>(t->len-4)) 
107     { fprintf(stderr,"GetU32() out of bounds: TagID = %i\n",t->id);
108       return 0;
109     }
110   #endif
111   res = t->data[t->pos]        | (t->data[t->pos+1]<<8) | 
112        (t->data[t->pos+2]<<16) | (t->data[t->pos+3]<<24);
113   t->pos+=4;
114   return res;
115 }
116
117 int swf_GetBlock(TAG * t,U8 * b,int l)
118 // returns number of bytes written (<=l)
119 // b = NULL -> skip data
120 { swf_ResetReadBits(t);
121   if ((t->len-t->pos)<l) l=t->len-t->pos;
122   if (b && l) memcpy(b,&t->data[t->pos],l);
123   t->pos+=l;
124   return l;
125 }
126
127 int swf_SetBlock(TAG * t,U8 * b,int l)
128 // Appends Block to the end of Tagdata, returns size
129 { U32 newlen = t->len + l;
130   swf_ResetWriteBits(t);
131   if (newlen>t->memsize)
132   { U32  newmem  = MEMSIZE(newlen);  
133     U8 * newdata = (U8*)((t->data)?realloc(t->data,newmem):malloc(newmem));
134     if (!newdata)
135     {
136       #ifdef DEBUG_RFXSWF
137         fprintf(stderr,"Fatal Error: malloc()/realloc() failed.\n");
138       #endif
139       return 0;
140     }
141     t->memsize = newmem;
142     t->data    = newdata;
143   }
144   if (b) memcpy(&t->data[t->len],b,l);
145   else memset(&t->data[t->len],0x00,l);
146   t->len+=l;
147   return l;
148 }
149
150 int swf_SetU8(TAG * t,U8 v)
151 { swf_ResetWriteBits(t);
152   if ((t->len+1)>t->memsize) return (swf_SetBlock(t,&v,1)==1)?0:-1;
153   t->data[t->len++] = v;
154   return 0;
155 }
156
157 int swf_SetU16(TAG * t,U16 v)
158 { U8 a[2];
159   a[0] = v&0xff;
160   a[1] = v>>8;
161   
162   swf_ResetWriteBits(t);
163   if ((t->len+2)>t->memsize) return (swf_SetBlock(t,a,2)==2)?0:-1;
164   t->data[t->len++] = a[0];
165   t->data[t->len++] = a[1];
166   return 0;
167 }
168
169 int swf_SetU32(TAG * t,U32 v)
170 { U8 a[4];
171   a[0] = v&0xff;        // to ensure correct handling of non-intel byteorder
172   a[1] = (v>>8)&0xff;
173   a[2] = (v>>16)&0xff;
174   a[3] = (v>>24)&0xff;
175   
176   swf_ResetWriteBits(t);
177   if ((t->len+4)>t->memsize) return (swf_SetBlock(t,a,4)==4)?0:-1;
178   t->data[t->len++] = a[0];
179   t->data[t->len++] = a[1];
180   t->data[t->len++] = a[2];
181   t->data[t->len++] = a[3];
182   return 0;
183 }
184
185 U32 swf_GetBits(TAG * t,int nbits)
186 { U32 res = 0;
187   if (!nbits) return 0;
188   if (!t->readBit) t->readBit = 0x80;
189   while (nbits)
190   { res<<=1;
191     if (t->data[t->pos]&t->readBit) res|=1;
192     t->readBit>>=1;
193     nbits--;
194     if (!t->readBit)
195     { if (nbits) t->readBit = 0x80;
196       #ifdef DEBUG_RFXSWF
197       if (t->pos>=t->len) 
198       { fprintf(stderr,"GetBits() out of bounds: TagID = %i\n",t->id);
199         return res;
200       }
201       #endif
202       t->pos++;
203     }
204   }
205   return res;
206 }
207
208 S32 swf_GetSBits(TAG * t,int nbits)
209 { U32 res = swf_GetBits(t,nbits);
210   if (res&(1<<(nbits-1))) res|=(0xffffffff<<nbits);  
211   return (S32)res;
212 }
213
214 int swf_SetBits(TAG * t,U32 v,int nbits)
215 { U32 bm = 1<<(nbits-1);
216
217   while (nbits)
218   { if (!t->writeBit)
219     { if (FAILED(swf_SetU8(t,0))) return -1;
220       t->writeBit = 0x80;
221     }
222     if (v&bm) t->data[t->len-1] |= t->writeBit;
223     bm>>=1;
224     t->writeBit>>=1;
225     nbits--;
226   }
227   return 0;
228 }
229
230 // Advanced Data Access Functions
231
232 int swf_SetRGB(TAG * t,RGBA * col)
233 { if (!t) return -1;
234   if (col)
235   { swf_SetU8(t,col->r);
236     swf_SetU8(t,col->g);
237     swf_SetU8(t,col->b);
238   } else swf_SetBlock(t,NULL,3);
239   return 0;
240 }
241
242 int swf_SetRGBA(TAG * t,RGBA * col)
243 { if (!t) return -1;
244   if (col)
245   { swf_SetU8(t,col->r);
246     swf_SetU8(t,col->g);
247     swf_SetU8(t,col->b);
248     swf_SetU8(t,col->a);
249   } else swf_SetBlock(t,NULL,4);
250   return 0;
251 }
252
253 int swf_CountBits(U32 v,int nbits)
254 { int n = 33;
255   U32 m = 0x80000000;
256   if (!v) n = 0; else
257   if (v&m)
258   { while (v&m)
259     { n--;
260       m>>=1;
261       if (!m) break;
262     } 
263   }
264   else
265   { while (!(v&m))
266     { n--;
267       m>>=1;
268       if (!m) break;
269     } 
270   }
271   return (n>nbits)?n:nbits;
272 }
273
274 int swf_GetRect(TAG * t,SRECT * r)
275 { int nbits;
276   SRECT dummy;
277   if (!r) r = &dummy;
278   nbits = (int) swf_GetBits(t,5);
279   r->xmin = swf_GetSBits(t,nbits);
280   r->xmax = swf_GetSBits(t,nbits);
281   r->ymin = swf_GetSBits(t,nbits);
282   r->ymax = swf_GetSBits(t,nbits);
283   return 0;
284 }
285
286 int swf_SetRect(TAG * t,SRECT * r)
287 { int nbits;
288     
289   nbits = swf_CountBits(r->xmin,0);
290   nbits = swf_CountBits(r->xmax,nbits);
291   nbits = swf_CountBits(r->ymin,nbits);
292   nbits = swf_CountBits(r->ymax,nbits);
293
294   swf_SetBits(t,nbits,5);
295   swf_SetBits(t,r->xmin,nbits);
296   swf_SetBits(t,r->xmax,nbits);
297   swf_SetBits(t,r->ymin,nbits);
298   swf_SetBits(t,r->ymax,nbits);
299
300   return 0;
301 }
302
303 int swf_GetMatrix(TAG * t,MATRIX * m)
304 { MATRIX dummy;
305   int nbits;
306     
307   if (!m) m = &dummy;
308   
309   if (!t)
310   { m->sx = m->sy = 0x10000;
311     m->r0 = m->r1 = 0;
312     m->tx = m->ty = 0;
313     return -1;
314   }
315
316   swf_ResetReadBits(t);
317   
318   if (swf_GetBits(t,1))
319   { nbits = swf_GetBits(t,5);
320     m->sx = swf_GetSBits(t,nbits);
321     m->sy = swf_GetSBits(t,nbits);
322   }
323   else m->sx = m->sy = 0x10000;
324   
325   if (swf_GetBits(t,1))
326   { nbits = swf_GetBits(t,5);
327     m->r0 = swf_GetSBits(t,nbits);
328     m->r1 = swf_GetSBits(t,nbits);
329   }
330   else m->r0 = m->r1 = 0x0;
331
332   nbits = swf_GetBits(t,5);
333   m->tx = swf_GetSBits(t,nbits);
334   m->ty = swf_GetSBits(t,nbits);
335   
336   return 0;
337 }
338
339 int swf_SetMatrix(TAG * t,MATRIX * m)
340 { int nbits;
341   MATRIX ma;
342
343   if (!m)
344   { m = &ma;
345     ma.sx = ma.sy = 0x10000;
346     ma.r0 = ma.r1 = 0;
347     ma.tx = ma.ty = 0;
348   }
349
350   swf_ResetWriteBits(t);
351
352   if ((m->sx==0x10000)&&(m->sy==0x10000)) swf_SetBits(t,0,1);
353   else
354   { swf_SetBits(t,1,1);
355     nbits = swf_CountBits(m->sx,0);
356     nbits = swf_CountBits(m->sy,nbits);
357     if(nbits>=32) {
358         fprintf(stderr,"rfxswf: Error: matrix values too large\n");
359         nbits = 31;
360     }
361     swf_SetBits(t,nbits,5);
362     swf_SetBits(t,m->sx,nbits);
363     swf_SetBits(t,m->sy,nbits);
364   }
365
366   if ((!m->r0)&&(!m->r1)) swf_SetBits(t,0,1);
367   else
368   { swf_SetBits(t,1,1);
369     nbits = swf_CountBits(m->r0,0);
370     nbits = swf_CountBits(m->r1,nbits);
371     if(nbits>=32) {
372         fprintf(stderr,"rfxswf: Error: matrix values too large\n");
373         nbits = 31;
374     }
375     swf_SetBits(t,nbits,5);
376     swf_SetBits(t,m->r0,nbits);
377     swf_SetBits(t,m->r1,nbits);
378   }
379
380   nbits = swf_CountBits(m->tx,0);
381   nbits = swf_CountBits(m->ty,nbits);
382   if(nbits>=32) {
383       fprintf(stderr,"rfxswf: Error: matrix values too large\n");
384       nbits = 31;
385   }
386   swf_SetBits(t,nbits,5);
387   swf_SetBits(t,m->tx,nbits);
388   swf_SetBits(t,m->ty,nbits);
389
390   return 0;
391 }
392
393 int swf_GetCXForm(TAG * t,CXFORM * cx,U8 alpha) //FIXME: alpha should be type bool
394 { CXFORM cxf;
395   int hasadd;
396   int hasmul;
397   int nbits;
398     
399   if (!cx) cx = &cxf;
400   
401   cx->a0 = cx->r0 = cx->g0 = cx->b0 = 256;
402   cx->a1 = cx->r1 = cx->g1 = cx->b1 = 0;
403
404   if (!t) return 0;
405   
406   swf_ResetReadBits(t);
407   hasadd = swf_GetBits(t,1);
408   hasmul = swf_GetBits(t,1);
409   nbits  = swf_GetBits(t,4);
410
411   if (hasmul)
412   { cx->r0 = (S16)swf_GetSBits(t,nbits);
413     cx->g0 = (S16)swf_GetSBits(t,nbits);
414     cx->b0 = (S16)swf_GetSBits(t,nbits);
415     if (alpha)
416       cx->a0 = (S16)swf_GetSBits(t,nbits);
417   }
418
419   if (hasadd)
420   { cx->r1 = (S16)swf_GetSBits(t,nbits);
421     cx->g1 = (S16)swf_GetSBits(t,nbits);
422     cx->b1 = (S16)swf_GetSBits(t,nbits);
423     if (alpha)
424       cx->a1 = (S16)swf_GetSBits(t,nbits);
425   }
426   
427   return 0;
428 }
429
430 int swf_SetCXForm(TAG * t,CXFORM * cx,U8 alpha)
431 { CXFORM cxf;
432   int hasadd;
433   int hasmul;
434   int nbits;
435     
436   if (!cx)
437   { cx = &cxf;
438     cx->a0 = cx->r0 = cx->g0 = cx->b0 = 256;
439     cx->a1 = cx->r1 = cx->g1 = cx->b1 = 0;
440   }
441
442   if (!alpha)
443   { cx->a0 = 256;
444     cx->a1 = 0;
445   }
446
447   nbits = 0;
448
449   hasmul = (cx->a0!=256)||(cx->r0!=256)||(cx->g0!=256)||(cx->b0!=256);
450   hasadd = cx->a1|cx->r1|cx->g1|cx->b1;
451
452   if (hasmul)
453   { if (alpha) nbits = swf_CountBits((S32)cx->a0,nbits);
454     nbits = swf_CountBits((S32)cx->r0,nbits);
455     nbits = swf_CountBits((S32)cx->g0,nbits);
456     nbits = swf_CountBits((S32)cx->b0,nbits);
457   }
458
459   if (hasadd)
460   { if (alpha) nbits = swf_CountBits((S32)cx->a1,nbits);
461     nbits = swf_CountBits((S32)cx->r1,nbits);
462     nbits = swf_CountBits((S32)cx->g1,nbits);
463     nbits = swf_CountBits((S32)cx->b1,nbits);
464   }
465   
466   swf_ResetWriteBits(t);
467   swf_SetBits(t,hasadd?1:0,1);
468   swf_SetBits(t,hasmul?1:0,1);
469   swf_SetBits(t,nbits,4);
470
471   if (hasmul)
472   { swf_SetBits(t,cx->r0,nbits);
473     swf_SetBits(t,cx->g0,nbits);
474     swf_SetBits(t,cx->b0,nbits);
475     if (alpha) swf_SetBits(t,cx->a0,nbits);
476   }
477
478   if (hasadd)
479   { swf_SetBits(t,cx->r1,nbits);
480     swf_SetBits(t,cx->g1,nbits);
481     swf_SetBits(t,cx->b1,nbits);
482     if (alpha) swf_SetBits(t,cx->a1,nbits);
483   }
484   
485   return 0;
486 }
487
488 int swf_GetPoint(TAG * t,SPOINT * p) { return 0; }
489 int swf_SetPoint(TAG * t,SPOINT * p) { return 0; }
490
491 // Tag List Manipulating Functions
492
493 int RFXSWF_UpdateFrame(TAG * t,S8 delta)
494 // returns number of frames
495 { int res = -1;
496   while (t)
497   { t->frame+=delta;
498     res = t->frame;
499     t = t->next;
500   }
501   return res;
502 }
503
504 #define swf_UpdateFrame(a,b) RFXSWF_UpdateFrame(a,b)
505
506 TAG * swf_InsertTag(TAG * after,U16 id)     // updates frames, if nescessary
507 { TAG * t;
508
509   t = (TAG *)malloc(sizeof(TAG));
510   if (t)
511   { memset(t,0x00,sizeof(TAG));
512     t->id = id;
513     
514     if (after)
515     { t->frame = after->frame;
516       t->prev  = after;
517       t->next  = after->next;
518       after->next = t;
519       if (t->next) t->next->prev = t;
520       
521       if (id==ST_SHOWFRAME) swf_UpdateFrame(t->next,+1);
522     }
523   }
524   return t;
525 }
526
527 int swf_DeleteTag(TAG * t)
528 { if (!t) return -1;
529
530   if (t->id==ST_SHOWFRAME) swf_UpdateFrame(t->next,-1);
531     
532   if (t->prev) t->prev->next = t->next;
533   if (t->next) t->next->prev = t->prev;
534
535   if (t->data) free(t->data);
536   free(t);
537   return 0;
538 }
539
540 TAG * RFXSWF_ReadTag(int handle,TAG * prev)
541 { TAG * t;
542   U16 raw;
543   U32 len;
544   int id;
545
546   if (read(handle,&raw,2)!=2) return NULL;
547
548   len = raw&0x3f;
549   id  = raw>>6;
550
551   if (len==0x3f)
552   { if (read(handle,&len,4)!=4) return NULL;
553   }
554
555   if (id==ST_DEFINESPRITE) len = 2*sizeof(U16);
556   // Sprite handling fix: Flaten sprite tree
557
558   t = (TAG *)malloc(sizeof(TAG));
559   
560   if (!t)
561   {
562     #ifdef DEBUG_RFXSWF
563       fprintf(stderr,"Fatal Error: malloc()/realloc() failed.\n");
564     #endif
565     return NULL;
566   }
567
568   memset(t,0x00,sizeof(TAG));
569   
570   t->len = len;
571   t->id  = id;
572
573   if (t->len)
574   { t->data = (U8*)malloc(t->len);
575     if (!t->data)
576     {
577       #ifdef DEBUG_RFXSWF
578         fprintf(stderr,"Fatal Error: malloc()/realloc() failed.\n");
579       #endif
580       return NULL;
581     }
582     t->memsize = t->len;
583     if (read(handle,t->data,t->len)!=t->len) return NULL;
584   }
585
586   if (prev)
587   { t->frame = prev->frame+((prev->id==ST_SHOWFRAME)?1:0);
588     t->prev  = prev;
589     prev->next = t;
590   }
591
592   return t;
593 }
594
595 int RFXSWF_DefineSprite_GetRealSize(TAG * t);
596
597 int RFXSWF_WriteTag(int handle,TAG * t)
598 // returns tag length in bytes (incl. Header), -1 = Error
599 // handle = -1 -> no output
600 { U16 raw[3];
601   U32 len;
602   int short_tag;
603
604   if (!t) return -1;
605
606   len = (t->id==ST_DEFINESPRITE)?RFXSWF_DefineSprite_GetRealSize(t):t->len;
607
608   short_tag = len<0x3f;
609
610   if (handle>=0)
611   { if (short_tag)
612     { raw[0] = len|((t->id&0x3ff)<<6);
613       if (write(handle,raw,2)!=2)
614       {
615         #ifdef DEBUG_RFXSWF
616           fprintf(stderr,"WriteTag() failed: Short Header.\n");
617         #endif
618         return -1;
619       }
620     }
621     else
622     { raw[0] = (t->id<<6)|0x3f;
623       raw[1] = (U16)(len&0xffff);
624       raw[2] = (U16)(len>>16);
625       if (write(handle,raw,6)!=6)
626       {
627         #ifdef DEBUG_RFXSWF
628           fprintf(stderr,"WriteTag() failed: Long Header.\n");
629         #endif
630         return -1;
631       }
632     }
633     
634     if (t->data)
635     { if (write(handle,t->data,t->len)!=t->len)
636       {
637         #ifdef DEBUG_RFXSWF
638           fprintf(stderr,"WriteTag() failed: Data.\n");
639         #endif
640         return -1;
641       }
642     }
643     #ifdef DEBUG_RFXSWF
644       else if (t->len) fprintf(stderr,"WriteTag(): Tag Data Error, id=%i\n",t->id);
645     #endif
646   }
647
648   return t->len+(short_tag?2:6);
649 }
650
651 int RFXSWF_DefineSprite_GetRealSize(TAG * t)
652 // Sprite Handling: Helper function to pack DefineSprite-Tag
653 { U32 len = t->len;
654   do
655   { t = swf_NextTag(t);
656     if (t->id!=ST_DEFINESPRITE) len += RFXSWF_WriteTag(-1,t);
657     else t = NULL;
658   } while (t&&(t->id!=ST_END));
659   return len;
660 }
661
662 #define swf_ReadTag(a,b)  RFXSWF_ReadTag(a,b)
663 #define swf_WriteTag(a,b) RFXSWF_WriteTag(a,b)
664
665 // Movie Functions
666
667 int swf_ReadSWF(int handle,SWF * swf)   // Reads SWF to memory (malloc'ed), returns length or <0 if fails
668 {     
669   if (!swf) return -1;
670   memset(swf,0x00,sizeof(SWF));
671
672   { char b[32];                         // read Header
673     TAG t1;
674     TAG * t;
675     
676     memset(&t1,0x00,sizeof(TAG));
677     
678     if ((t1.len=read(handle,b,32))<21) return -1;
679     t1.data = (U8*)b;
680
681     if (swf_GetU8(&t1)!=(U8)'F') return -1;
682     if (swf_GetU8(&t1)!=(U8)'W') return -1;
683     if (swf_GetU8(&t1)!=(U8)'S') return -1;
684
685     swf->fileVersion = swf_GetU8(&t1);
686     swf->fileSize    = swf_GetU32(&t1);
687     swf_GetRect(&t1,&swf->movieSize);
688     swf->frameRate   = swf_GetU16(&t1);
689     swf->frameCount  = swf_GetU16(&t1);
690
691     swf_GetU8(&t1);
692     lseek(handle,swf_GetTagPos(&t1)-1,SEEK_SET);
693   
694                                         // reda tags and connect to list
695     t = &t1;
696     while (t) t = swf_ReadTag(handle,t);
697     swf->firstTag = t1.next;
698     t1.next->prev = NULL;
699   }
700   
701   return 0;
702 }
703 int  swf_WriteSWF(int handle,SWF * swf)     // Writes SWF to file, returns length or <0 if fails
704 { U32 len;
705   TAG * t;
706     
707   if (!swf) return -1;
708
709   // Insert REFLEX Tag
710
711 #ifdef INSERT_RFX_TAG
712
713   if (swf_NextTag(swf->firstTag))
714     if (swf_GetTagID(swf_NextTag(swf->firstTag))!=ST_REFLEX)
715       swf_SetBlock(swf_InsertTag(swf->firstTag,ST_REFLEX),"rfx",3);
716
717 #endif // INSERT_RFX_TAG
718
719   // Count Frames + File Size
720
721   len = 0;
722   t = swf->firstTag;
723   swf->frameCount = 0;
724
725   while(t)
726   { len += swf_WriteTag(-1,t);
727     if (t->id==ST_SHOWFRAME) swf->frameCount++;
728     t = swf_NextTag(t);
729   }
730   
731   { TAG t1;
732     char b[64];
733     U32 l;
734
735     memset(&t1,0x00,sizeof(TAG));
736     t1.data    = (U8*)b;
737     t1.memsize = 64;
738     
739     swf_SetU8(&t1,'F');      
740     swf_SetU8(&t1,'W');      
741     swf_SetU8(&t1,'S');
742     swf_SetU8(&t1,swf->fileVersion);
743     
744     swf_SetU32(&t1,0);                      // Keep space for filesize
745     swf_SetRect(&t1,&swf->movieSize);
746     swf_SetU16(&t1,swf->frameRate);
747     swf_SetU16(&t1,swf->frameCount);
748
749     l = swf_GetDataSize(&t1);
750     swf->fileSize = l+len;
751     t1.len = 4;                         // bad & ugly trick !
752     swf_SetU32(&t1,swf->fileSize);
753
754     if (handle>=0)
755     { 
756       int ret = write(handle,b,l);
757       if (ret!=l)
758       {
759         #ifdef DEBUG_RFXSWF
760           printf("ret:%d (fd:%d)\n",ret, handle);
761           perror("write:");
762           fprintf(stderr,"WriteSWF() failed: Header.\n");
763         #endif
764         return -1;
765       }
766
767       t = swf->firstTag;
768       while (t)
769       { if (swf_WriteTag(handle,t)<0) return -1;
770         t = swf_NextTag(t);
771       }
772     }
773   }
774   return (int)swf->fileSize;
775 }
776
777 int swf_WriteCGI(SWF * swf)
778 { int len;
779   char s[1024];
780     
781   len = swf_WriteSWF(-1,swf);
782
783   if (len<0) return -1;
784
785   sprintf(s,"Content-type: application/x-shockwave-flash\n"
786             "Accept-Ranges: bytes\n"
787             "Content-Length: %lu\n"
788             "Expires: Thu, 13 Apr 2000 23:59:59 GMT\n"
789             "\n",len);
790             
791   write(fileno(stdout),s,strlen(s));
792   return swf_WriteSWF(fileno(stdout),swf);
793 }
794
795 void swf_FreeTags(SWF * swf)                 // Frees all malloc'ed memory for tags
796 { TAG * t = swf->firstTag;
797
798   while (t)
799   { TAG * tnew = t->next;
800     if (t->data) free(t->data);
801     free(t);
802     t = tnew;
803   }
804 }
805
806 // include advanced functions
807
808 #ifdef __NT__
809
810 #include "modules\swfdump.c"
811 #include "modules\swfshape.c"
812 #include "modules\swftext.c"
813 #include "modules\swfobject.c"
814 #include "modules\swfbutton.c"
815 #include "modules\swftools.c"
816 #include "modules\swfcgi.c"
817 #include "modules\swfbits.c"
818 #include "modules\swfaction.c"
819
820 #else
821
822 #include "modules/swfdump.c"
823 #include "modules/swfshape.c"
824 #include "modules/swftext.c"
825 #include "modules/swfobject.c"
826 #include "modules/swfbutton.c"
827 #include "modules/swftools.c"
828 #include "modules/swfcgi.c"
829 #include "modules/swfbits.c"
830 #include "modules/swfaction.c"
831
832 #endif
833
834