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