fixed framerate calculation.
[swftools.git] / src / swfc.c
1 /* swfc.c
2    Compiles swf code (.sc) files into .swf files.
3
4    Part of the swftools package.
5
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <memory.h>
27 #include <errno.h>
28 #include <math.h>
29 #include "../config.h"
30 #include "../lib/rfxswf.h"
31 #include "../lib/log.h"
32 #include "../lib/args.h"
33 #include "../lib/q.h"
34 #include "parser.h"
35 #include "wav.h"
36
37 //#define DEBUG
38
39 static char * filename = 0;
40 static char * outputname = "output.swf";
41 static int verbose = 2;
42 static int override_outputname = 0;
43
44 static struct options_t options[] =
45 {
46  {"o","output"},
47  {"v","verbose"},
48  {"V","version"},
49  {0,0}
50 };
51     
52 int args_callback_option(char*name,char*val)
53 {
54     if(!strcmp(name, "V")) {
55         printf("swfc - part of %s %s\n", PACKAGE, VERSION);
56         exit(0);
57     }
58     else if(!strcmp(name, "o")) {
59         outputname = val;
60         override_outputname = 1;
61         return 1;
62     }
63     else if(!strcmp(name, "v")) {
64         verbose ++;
65         return 0;
66     }
67     else {
68         printf("Unknown option: -%s\n", name);
69         exit(1);
70     }
71     return 0;
72 }
73 int args_callback_longoption(char*name,char*val)
74 {
75     return args_long2shortoption(options, name, val);
76 }
77 void args_callback_usage(char*name)
78 {
79     printf("Usage: %s [-o filename] file.wav\n", name);
80     printf("\t-v , --verbose\t\t\t Be more verbose\n");
81     printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
82     printf("\t-V , --version\t\t\t Print program version and exit\n");
83 }
84 int args_callback_command(char*name,char*val)
85 {
86     if(filename) {
87         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
88                  filename, name);
89     }
90     filename = name;
91     return 0;
92 }
93
94 static struct token_t* file;
95
96 static int pos;
97 static char*text;
98 static int textlen;
99 static int type;
100 static int line;
101 static int column;
102
103 static void syntaxerror(char*format, ...)
104 {
105     char buf[1024];
106     va_list arglist;
107     va_start(arglist, format);
108     vsprintf(buf, format, arglist);
109     va_end(arglist);
110     printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
111     exit(1);
112 }
113
114 static void warning(char*format, ...)
115 {
116     char buf[1024];
117     va_list arglist;
118     va_start(arglist, format);
119     vsprintf(buf, format, arglist);
120     va_end(arglist);
121     printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
122 }
123    
124 static void readToken()
125 {
126     type = file[pos].type;
127     if(type == END) {
128         syntaxerror("unexpected end of file");
129     }
130     text = file[pos].text;
131     textlen = strlen(text);
132     line = file[pos].line;
133     column = file[pos].column;
134     pos++;
135     //printf("---> %d(%s) %s\n", type, type_names[type], text);
136 }
137
138 static void pushBack()
139 {
140     int p;
141     if(!pos) syntaxerror("internal error 3");
142     pos--;
143     p = pos;
144     if(p) p--;
145     text = file[p].text;
146     textlen = strlen(text);
147     type = file[p].type;
148     line = file[p].line;
149     column = file[p].column;
150 }
151
152 static int noMoreTokens()
153 {
154     if(file[pos].type == END)
155         return 1;
156     return 0;
157 }
158
159 // ------------------------------ swf routines ----------------------------
160 struct _character;
161 static struct level
162 {
163    int type; //0=swf, 1=sprite, 2=clip
164
165    /* for swf (0): */
166    SWF*swf;
167    char*filename; 
168
169    /* for sprites (1): */
170    TAG*tag;
171    U16 id;
172    char*name;
173    U16 olddepth;
174    int oldframe;
175    dictionary_t oldinstances;
176    SRECT oldrect;
177
178 } stack[256];
179 static int stackpos = 0;
180
181 static dictionary_t characters;
182 static char idmap[65536];
183 static TAG*tag = 0; //current tag
184
185 static int id; //current character id
186 static int currentframe; //current frame in current level
187 static SRECT currentrect; //current bounding box in current level
188 static U16 currentdepth;
189 static dictionary_t instances;
190 static dictionary_t fonts;
191 static dictionary_t sounds;
192
193 typedef struct _parameters {
194     int x,y; 
195     float scalex, scaley; 
196     CXFORM cxform;
197     float rotate;
198     float shear;
199     SPOINT pivot;
200     SPOINT pin;
201 } parameters_t;
202
203 typedef struct _character {
204     TAG*definingTag;
205     U16 id;
206     SRECT size;
207 } character_t;
208
209 typedef struct _instance {
210     character_t*character;
211     U16 depth;
212     parameters_t parameters;
213     TAG* lastTag; //last tag which set the object
214     U16 lastFrame; //frame lastTag is in
215 } instance_t;
216
217 static void character_init(character_t*c)
218 {
219     memset(c, 0, sizeof(character_t));
220 }
221 static character_t* character_new()
222 {
223     character_t*c;
224     c = (character_t*)malloc(sizeof(character_t));
225     character_init(c);
226     return c;
227 }
228 static void instance_init(instance_t*i)
229 {
230     memset(i, 0, sizeof(instance_t));
231 }
232 static instance_t* instance_new()
233 {
234     instance_t*c;
235     c = (instance_t*)malloc(sizeof(instance_t));
236     instance_init(c);
237     return c;
238 }
239
240 static void incrementid()
241 {
242     while(idmap[++id]) {
243         if(id==65535)
244             syntaxerror("Out of character ids.");
245     }
246     idmap[id] = 1;
247 }
248
249 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
250 {
251     character_t* c = character_new();
252     
253     c->definingTag = ctag;
254     c->id = id;
255     c->size = r;
256     if(dictionary_lookup(&characters, name))
257         syntaxerror("character %s defined twice", name);
258     dictionary_put2(&characters, name, c);
259
260     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
261     swf_SetU16(tag, id);
262     swf_SetString(tag, name);
263     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
264     swf_SetU16(tag, 1);
265     swf_SetU16(tag, id);
266     swf_SetString(tag, name);
267 }
268 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
269 {
270     instance_t* i = instance_new();
271     i->character = c;
272     i->depth = depth;
273     //swf_GetMatrix(0, &i->matrix);
274     if(dictionary_lookup(&instances, name))
275         syntaxerror("object %s defined twice", name);
276     dictionary_put2(&instances, name, i);
277     return i;
278 }
279
280 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
281 {
282     p->x = x; p->y = y; 
283     p->scalex = scalex; p->scaley = scaley;
284     p->pin    = pin; p->pivot = pivot;
285     p->rotate = rotate; p->cxform = cxform;
286     p->shear = shear;
287 }
288
289 static void parameters_clear(parameters_t*p)
290 {
291     p->x = 0; p->y = 0; 
292     p->scalex = 1.0; p->scaley = 1.0;
293     p->pin.x = 1; p->pin.y = 0;
294     p->pivot.x = 0; p->pivot.y = 0;
295     p->rotate = 0; 
296     p->shear = 0; 
297     swf_GetCXForm(0, &p->cxform, 1);
298 }
299
300 static void makeMatrix(MATRIX*m, parameters_t*p)
301 {
302     SPOINT h;
303     float sx,r1,r0,sy;
304
305     /*        /sx r1\ /x\ 
306      *        \r0 sy/ \y/
307      */
308
309     sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979);
310     r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
311     r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979);
312     sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
313
314     m->sx = (int)(sx*65536+0.5);
315     m->r1 = (int)(r1*65536+0.5);
316     m->r0 = (int)(r0*65536+0.5);
317     m->sy = (int)(sy*65536+0.5);
318
319     m->tx = m->ty = 0;
320
321     h = swf_TurnPoint(p->pin, m);
322     m->tx = p->x - h.x;
323     m->ty = p->y - h.y;
324 }
325
326 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
327 {
328     MATRIX m;
329     SRECT r;
330     makeMatrix(&m, p);
331     r = swf_TurnRect(i->character->size, &m);
332     if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
333        currentrect.xmax == 0 && currentrect.ymax == 0)
334         currentrect = r;
335     else
336         swf_ExpandRect2(&currentrect, &r);
337     return m;
338 }
339
340 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
341 {
342     SWF*swf = (SWF*)malloc(sizeof(SWF));
343
344     if(stackpos)
345         syntaxerror(".swf blocks can't be nested");
346
347     memset(swf, 0, sizeof(swf));
348     swf->fileVersion = version;
349     swf->movieSize = r;
350     swf->frameRate = fps;
351     swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
352     swf->compressed = compress;
353     swf_SetRGB(tag,&background);
354     
355     if(stackpos==sizeof(stack)/sizeof(stack[0]))
356         syntaxerror("too many levels of recursion");
357     
358     dictionary_init(&characters);
359     dictionary_init(&instances);
360     dictionary_init(&fonts);
361     dictionary_init(&sounds);
362
363     memset(&stack[stackpos], 0, sizeof(stack[0]));
364     stack[stackpos].type = 0;
365     stack[stackpos].filename = strdup(name);
366     stack[stackpos].swf = swf;
367     stack[stackpos].oldframe = -1;
368     stackpos++;
369     id = 0;
370
371     currentframe = 0;
372     memset(&currentrect, 0, sizeof(currentrect));
373     currentdepth = 1;
374     
375     memset(idmap, 0, sizeof(idmap));
376     incrementid();
377 }
378
379 void s_sprite(char*name)
380 {
381     tag = swf_InsertTag(tag, ST_DEFINESPRITE);
382     swf_SetU16(tag, id); //id
383     swf_SetU16(tag, 0); //frames
384
385     memset(&stack[stackpos], 0, sizeof(stack[0]));
386     stack[stackpos].type = 1;
387     stack[stackpos].oldframe = currentframe;
388     stack[stackpos].olddepth = currentdepth;
389     stack[stackpos].oldrect = currentrect;
390     stack[stackpos].oldinstances = instances;
391     stack[stackpos].tag = tag;
392     stack[stackpos].id = id;
393     stack[stackpos].name = strdup(name);
394    
395     /* FIXME: those four fields should be bundled together */
396     dictionary_init(&instances);
397     currentframe = 0;
398     currentdepth = 1;
399     memset(&currentrect, 0, sizeof(currentrect));
400
401     stackpos++;
402     incrementid();
403 }
404
405 static void s_endSprite()
406 {
407     SRECT r = currentrect;
408     stackpos--;
409    
410     /* TODO: before clearing, prepend "<spritename>." to names and
411              copy into old instances dict */
412     dictionary_clear(&instances);
413
414     currentframe = stack[stackpos].oldframe;
415     currentrect = stack[stackpos].oldrect;
416     currentdepth = stack[stackpos].olddepth;
417     instances = stack[stackpos].oldinstances;
418     tag = swf_InsertTag(tag, ST_END);
419
420     tag = stack[stackpos].tag;
421     swf_FoldSprite(tag);
422     if(tag->next != 0)
423         syntaxerror("internal error(7)");
424
425     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
426     free(stack[stackpos].name);
427 }
428
429 static void s_endSWF()
430 {
431     int fi;
432     SWF* swf;
433     char*filename;
434     stackpos--;
435
436     swf = stack[stackpos].swf;
437     filename = stack[stackpos].filename;
438     
439     tag = swf_InsertTag(tag, ST_SHOWFRAME);
440     tag = swf_InsertTag(tag, ST_END);
441
442     swf_OptimizeTagOrder(swf);
443
444     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
445         swf->movieSize = currentrect; /* "autocrop" */
446     
447     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
448         swf->movieSize.xmax += 20; /* 1 by 1 pixels */
449         swf->movieSize.ymax += 20;
450     }
451
452     fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
453     if(fi<0) {
454         syntaxerror("couldn't create output file %s", filename);
455     }
456     if(swf->compressed) 
457         {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
458     else
459         {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
460
461     close(fi);
462     
463     dictionary_clear(&instances);
464     dictionary_clear(&characters);
465     dictionary_clear(&fonts);
466     dictionary_clear(&sounds);
467
468     swf_FreeTags(swf);
469     free(swf);
470     free(filename);
471 }
472 void s_close()
473 {
474     if(stackpos) {
475         if(stack[stackpos-1].type == 0)
476             syntaxerror("End of file encountered in .swf block");
477         if(stack[stackpos-1].type == 1)
478             syntaxerror("End of file encountered in .sprite block");
479         if(stack[stackpos-1].type == 2)
480             syntaxerror("End of file encountered in .clip block");
481     }
482 }
483
484 int s_getframe()
485 {
486     return currentframe;
487 }
488
489 void s_frame(int nr)
490 {
491     int t;
492     for(t=currentframe;t<nr;t++) {
493         tag = swf_InsertTag(tag, ST_SHOWFRAME);
494     }
495     currentframe = nr;
496 }
497         
498 RGBA black={r:0,g:0,b:0,a:0};
499 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
500 {
501     SRECT r;
502     SHAPE* s;
503     int ls1,fs1=0;
504     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
505     swf_ShapeNew(&s);
506     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
507     if(dofill)
508         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
509     swf_SetU16(tag,id);
510     r.xmin = -linewidth-linewidth/2;
511     r.ymin = -linewidth-linewidth/2;
512     r.xmax = width+linewidth+linewidth/2;
513     r.ymax = height+linewidth+linewidth/2;
514     swf_SetRect(tag,&r);
515     swf_SetShapeHeader(tag,s);
516     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
517     swf_ShapeSetLine(tag,s,width,0);
518     swf_ShapeSetLine(tag,s,0,height);
519     swf_ShapeSetLine(tag,s,-width,0);
520     swf_ShapeSetLine(tag,s,0,-height);
521     swf_ShapeSetEnd(tag);
522     swf_ShapeFree(s);
523    
524     s_addcharacter(name, id, tag, r);
525     incrementid();
526 }
527
528 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
529 {
530     SRECT rect;
531     SHAPE* s;
532     int ls1,fs1=0;
533     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
534     swf_ShapeNew(&s);
535     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
536     if(dofill)
537         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
538     swf_SetU16(tag,id);
539     rect.xmin = -linewidth-linewidth/2;
540     rect.ymin = -linewidth-linewidth/2;
541     rect.xmax = 2*r+linewidth+linewidth/2;
542     rect.ymax = 2*r+linewidth+linewidth/2;
543
544     swf_SetRect(tag,&rect);
545     swf_SetShapeHeader(tag,s);
546     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
547     swf_ShapeSetCircle(tag, s, r,r,r,r);
548     swf_ShapeSetEnd(tag);
549     swf_ShapeFree(s);
550    
551     s_addcharacter(name, id, tag, rect);
552     incrementid();
553 }
554
555 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
556 {
557     SRECT rect;
558     SHAPE* s;
559     int ls1,fs1=0;
560     int g;
561     U8*text = (U8*)_text;
562
563     SWFFONT*font;
564     font = dictionary_lookup(&fonts, fontname);
565     if(!font)
566         syntaxerror("font \"%s\" not known!", fontname);
567     if(!dofill)
568         syntaxerror("textshapes must be filled", fontname);
569
570     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
571         warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
572         s_box(name, 0, 0, black, 20, black, 0);
573         return;
574     }
575     g = font->ascii2glyph[text[0]];
576     rect = font->layout->bounds[g];
577
578     swf_ShapeNew(&s);
579     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
580     if(dofill)
581         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
582      
583     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
584     swf_SetU16(tag,id);
585     swf_SetRect(tag, &rect);
586     swf_SetShapeStyles(tag, s);
587     swf_SetSimpleShape(tag, font->glyph[g].shape);
588     swf_ShapeFree(s);
589     
590     s_addcharacter(name, id, tag, rect);
591     incrementid();
592 }
593 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
594 {
595     SRECT r;
596
597     SWFFONT*font;
598     font = dictionary_lookup(&fonts, fontname);
599     if(!font)
600         syntaxerror("font \"%s\" not known!", fontname);
601     
602     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
603     swf_SetU16(tag, id);
604     if(!font->numchars) {
605         s_box(name, 0, 0, black, 20, black, 0);
606         return;
607     }
608     r = swf_SetDefineText(tag, font, &color, text, size);
609    
610     s_addcharacter(name, id, tag, r);
611     incrementid();
612 }
613
614 void dumpSWF(SWF*swf)
615 {
616     TAG* tag = swf->firstTag;
617     printf("vvvvvvvvvvvvvvvvvvvvv\n");
618     while(tag) {
619         printf("%8d %s\n", tag->len, swf_TagGetName(tag));
620         tag = tag->next;
621     }
622     printf("^^^^^^^^^^^^^^^^^^^^^\n");
623 }
624     
625 void s_font(char*name, char*filename)
626 {
627     int f;
628     SWF swf;
629     SWFFONT* font;
630     f = open(filename,O_RDONLY|O_BINARY);
631     if (f<0) { 
632         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
633         font = (SWFFONT*)malloc(sizeof(SWFFONT));
634         memset(font, 0, sizeof(SWFFONT));
635         dictionary_put2(&fonts, name, font);
636         return;
637     }
638     font = 0;
639     if (swf_ReadSWF(f,&swf)>=0) { 
640         swf_FontExtract(&swf, 0x4e46, &font);
641         swf_FreeTags(&swf);
642     }
643     close(f);
644     if (font==0) { 
645         syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
646     }
647
648     if(0)
649     {
650         /* fix the layout. Only needed for old fonts */
651         int t;
652         for(t=0;t<font->numchars;t++) {
653             font->glyph[t].advance = 0;
654         }
655         font->layout = 0;
656         swf_FontCreateLayout(font);
657     }
658
659     font->id = id;
660     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
661     swf_FontSetDefine2(tag, font);
662     incrementid();
663
664     if(dictionary_lookup(&fonts, name))
665         syntaxerror("font %s defined twice", name);
666     dictionary_put2(&fonts, name, font);
667 }
668
669 typedef struct _sound_t
670 {
671     U16 id;
672     TAG*tag;
673 } sound_t;
674
675 void s_sound(char*name, char*filename)
676 {
677     struct WAV wav, wav2;
678     sound_t* sound;
679     U16*samples;
680     int numsamples;
681
682     if(!readWAV(filename, &wav)) {
683         warning("Couldn't read wav file \"%s\"", filename);
684         samples = 0;
685         numsamples = 0;
686     } else {
687         convertWAV2mono(&wav, &wav2, 44100);
688         samples = (U16*)wav2.data;
689         numsamples = wav2.size/2;
690         free(wav.data);
691     }
692
693     tag = swf_InsertTag(tag, ST_DEFINESOUND);
694     swf_SetU16(tag, id); //id
695     swf_SetSoundDefine(tag, samples, numsamples);
696    
697     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
698     sound->tag = tag;
699     sound->id = id;
700
701     if(dictionary_lookup(&sounds, name))
702         syntaxerror("sound %s defined twice", name);
703     dictionary_put2(&sounds, name, sound);
704     
705     incrementid();
706
707     if(samples)
708         free(samples);
709 }
710
711 void s_playsound(char*name, int loops, int nomultiple, int stop)
712 {
713     sound_t* sound = dictionary_lookup(&sounds, name);
714     SOUNDINFO info;
715     if(!sound)
716         syntaxerror("Don't know anything about sound \"%s\"", name);
717
718     tag = swf_InsertTag(tag, ST_STARTSOUND);
719     swf_SetU16(tag, sound->id); //id
720     memset(&info, 0, sizeof(info));
721     info.stop = stop;
722     info.loops = loops;
723     info.nomultiple = nomultiple;
724     swf_SetSoundInfo(tag, &info);
725 }
726
727 void s_shape(char*name, char*filename)
728 {
729     int f;
730     SWF swf;
731     TAG* ftag;
732     SRECT r;
733     TAG* s;
734     int level = 0;
735     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
736     f = open(filename,O_RDONLY);
737     if (f<0) { 
738         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
739         s_box(name, 0, 0, black, 20, black, 0);
740         return;
741     }
742     if (swf_ReadSWF(f,&swf)<0) { 
743         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
744         s_box(name, 0, 0, black, 20, black, 0);
745         return;
746     }
747     close(f);
748
749     /* FIXME: The following sets the bounding Box for the character. 
750               It is wrong for two reasons:
751               a) It may be too small (in case objects in the movie clip at the borders)
752               b) it may be too big (because the poor movie never got autocropped)
753     */
754     r = swf.movieSize;
755     
756     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
757     swf_SetU16(tag, id);
758     swf_SetU16(tag, 0);
759
760     swf_Relocate(&swf, idmap);
761
762     ftag = swf.firstTag;
763     level = 1;
764     while(ftag) {
765         int t;
766         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
767             if(cutout[t] == ftag->id) {
768                 ftag = ftag->next;
769                 continue;
770             }
771         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
772             level++;
773         if(ftag->id == ST_END)
774             level--;
775         if(!level)
776             break;
777         /* We simply dump all tags right after the sprite
778            header, relying on the fact that swf_OptimizeTagOrder() will
779            sort things out for us later. 
780            We also rely on the fact that the imported SWF is well-formed.
781          */
782         tag = swf_InsertTag(tag, ftag->id);
783         swf_SetBlock(tag, ftag->data, ftag->len);
784         ftag = ftag->next;
785     }
786     if(!ftag)
787         syntaxerror("Included file %s contains errors", filename);
788     tag = swf_InsertTag(tag, ST_END);
789
790     swf_FreeTags(&swf);
791
792     s_addcharacter(name, id, tag, r);
793     incrementid();
794 }
795 SRECT s_getCharBBox(char*name)
796 {
797     character_t* c = dictionary_lookup(&characters, name);
798     if(!c) syntaxerror("character '%s' unknown(2)", name);
799     return c->size;
800 }
801 SRECT s_getInstanceBBox(char*name)
802 {
803     instance_t * i = dictionary_lookup(&instances, name);
804     character_t * c;
805     if(!i) syntaxerror("instance '%s' unknown(4)", name);
806     c = i->character;
807     if(!c) syntaxerror("internal error(5)");
808     return c->size;
809 }
810 parameters_t s_getParameters(char*name)
811 {
812     instance_t * i = dictionary_lookup(&instances, name);
813     if(!i) syntaxerror("instance '%s' unknown(10)", name);
814     return i->parameters;
815 }
816 void s_startclip(char*instance, char*character, parameters_t p)
817 {
818     character_t* c = dictionary_lookup(&characters, character);
819     instance_t* i;
820     MATRIX m;
821     if(!c) {
822         syntaxerror("character %s not known", character);
823     }
824     i = s_addinstance(instance, c, currentdepth);
825     i->parameters = p;
826     m = s_instancepos(i, &p);
827     
828     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
829     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
830     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
831     i->lastTag = tag;
832     i->lastFrame= currentframe;
833
834     stack[stackpos].tag = tag;
835     stack[stackpos].type = 2;
836     stackpos++;
837
838     currentdepth++;
839 }
840 void s_endClip()
841 {
842     SWFPLACEOBJECT p;
843     stackpos--;
844     swf_SetTagPos(stack[stackpos].tag, 0);
845     swf_GetPlaceObject(stack[stackpos].tag, &p);
846     p.clipdepth = currentdepth;
847     swf_ClearTag(stack[stackpos].tag);
848     swf_SetPlaceObject(stack[stackpos].tag, &p);
849     currentdepth++;
850 }
851
852 void s_put(char*instance, char*character, parameters_t p)
853 {
854     character_t* c = dictionary_lookup(&characters, character);
855     instance_t* i;
856     MATRIX m;
857     if(!c) {
858         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
859     }
860     
861     i = s_addinstance(instance, c, currentdepth);
862     i->parameters = p;
863     m = s_instancepos(i, &p);
864     
865     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
866     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
867     i->lastTag = tag;
868     i->lastFrame = currentframe;
869     currentdepth++;
870 }
871
872 void s_jump(char*instance, parameters_t p)
873 {
874     instance_t* i = dictionary_lookup(&instances, instance);
875     MATRIX m;
876     if(!i) {
877         syntaxerror("instance %s not known", instance);
878     }
879
880     i->parameters = p;
881     m = s_instancepos(i, &p);
882
883     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
884     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
885     i->lastTag = tag;
886     i->lastFrame = currentframe;
887 }
888
889 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
890 {
891     parameters_t p;
892     float ratio;
893     if(num==0 || num==1)
894         return *p1;
895     ratio = (float)pos/(float)num;
896     
897     p.x = (p2->x-p1->x)*ratio + p1->x;
898     p.y = (p2->y-p1->y)*ratio + p1->y;
899     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
900     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
901     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
902     p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
903
904     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
905     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
906     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
907     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
908
909     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
910     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
911     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
912     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
913
914     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
915     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
916     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
917     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
918     return p;
919 }
920
921 void s_change(char*instance, parameters_t p2)
922 {
923     instance_t* i = dictionary_lookup(&instances, instance);
924     MATRIX m;
925     parameters_t p1;
926     TAG*t;
927     int frame, allframes;
928     if(!i) {
929         syntaxerror("instance %s not known", instance);
930     }
931     p1 = i->parameters;
932     
933     allframes = currentframe - i->lastFrame - 1;
934     if(allframes < 0) {
935         warning(".change ignored. can only .put/.change an object once per frame.");
936         return;
937     }
938     
939     m = s_instancepos(i, &p2);
940     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
941     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
942     i->parameters = p2;
943
944     /* o.k., we got the start and end point set. Now iterate though all the
945        tags in between, inserting object changes after each new frame */
946     t = i->lastTag;
947     i->lastTag = tag;
948     if(!t) syntaxerror("internal error(6)");
949     frame = 0;
950     while(frame < allframes) {
951         if(t->id == ST_SHOWFRAME) {
952             parameters_t p;
953             MATRIX m;
954             TAG*lt;
955             frame ++;
956             p = s_interpolate(&p1, &p2, frame, allframes);
957             m = s_instancepos(i, &p); //needed?
958             lt = swf_InsertTag(t, ST_PLACEOBJECT2);
959             i->lastFrame = currentframe;
960             swf_ObjectMove(lt, i->depth, &m, &p.cxform);
961             t = lt;
962             if(frame == allframes)
963                 break;
964         }
965         t = t->next;
966         if(!t) 
967             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
968     }
969 }
970
971 void s_delinstance(char*instance)
972 {
973     instance_t* i = dictionary_lookup(&instances, instance);
974     if(!i) {
975         syntaxerror("instance %s not known", instance);
976     }
977     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
978     swf_SetU16(tag, i->depth);
979     dictionary_del(&instances, instance);
980 }
981
982 void s_qchange(char*instance, parameters_t p)
983 {
984 }
985
986 void s_end()
987 {
988     if(!stackpos)
989         syntaxerror(".end unexpected");
990     if(stack[stackpos-1].type == 0)
991         s_endSWF();
992     else if(stack[stackpos-1].type == 1)
993         s_endSprite();
994     else if(stack[stackpos-1].type == 2)
995         s_endClip();
996     else syntaxerror("internal error 1");
997 }
998
999 // ------------------------------------------------------------------------
1000
1001 typedef int command_func_t(map_t*args);
1002
1003 SRECT parseBox(char*str)
1004 {
1005     SRECT r;
1006     float xmin, xmax, ymin, ymax;
1007     char*x = strchr(str, 'x');
1008     char*d1=0,*d2=0;
1009     if(!strcmp(str, "autocrop")) {
1010         r.xmin = r.ymin = r.xmax = r.ymax = 0;
1011         return r;
1012     }
1013     if(!x) goto error;
1014     d1 = strchr(x+1, ':');
1015     if(d1)
1016         d2 = strchr(d1+1, ':');
1017     if(!d1) {
1018         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1019             goto error;
1020         xmin = ymin = 0;
1021     }
1022     else if(d1 && !d2) {
1023         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1024             goto error;
1025         xmax += xmin;
1026         ymin = 0;
1027     }
1028     else {
1029         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1030             goto error;
1031         xmax += xmin;
1032         ymax += ymin;
1033     }
1034     r.xmin = (SCOORD)(xmin*20);
1035     r.ymin = (SCOORD)(ymin*20);
1036     r.xmax = (SCOORD)(xmax*20);
1037     r.ymax = (SCOORD)(ymax*20);
1038     return r;
1039 error:
1040     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1041     return r;
1042 }
1043 float parseFloat(char*str)
1044 {
1045     return atof(str);
1046 }
1047 int parseInt(char*str)
1048 {
1049     int t;
1050     int l=strlen(str);
1051     int s=0;
1052     if(str[0]=='+' || str[0]=='-')
1053         s++;
1054
1055     for(t=s;t<l;t++)
1056         if(str[t]<'0' || str[t]>'9')
1057             syntaxerror("Not an Integer: \"%s\"", str);
1058     return atoi(str);
1059 }
1060 int parseTwip(char*str)
1061 {
1062     char*dot;
1063     int sign=1;
1064     if(str[0]=='+' || str[0]=='-') {
1065         if(str[0]=='-')
1066             sign = -1;
1067         str++;
1068     }
1069     dot = strchr(str, '.');
1070     if(!dot) {
1071         int l=strlen(str);
1072         int t;
1073         return sign*parseInt(str)*20;
1074     } else {
1075         int l=strlen(++dot);
1076         char*s;
1077         for(s=str;s<dot-1;s++)
1078             if(*s<'0' || *s>'9')
1079                 syntaxerror("Not a coordinate: \"%s\"", str);
1080         for(s=dot;*s;s++) {
1081             if(*s<'0' || *s>'9')
1082                 syntaxerror("Not a coordinate: \"%s\"", str);
1083         }
1084         if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1085             warning("precision loss: %s converted to twip", str);
1086             dot[2] = 0;
1087             l=2;
1088         }
1089         if(l==0)
1090             return sign*atoi(str)*20;
1091         if(l==1)
1092             return sign*atoi(str)*20+atoi(dot)*2;
1093         if(l==2)
1094             return sign*atoi(str)*20+atoi(dot)/5;
1095     }
1096     return 0;
1097 }
1098
1099 int isPoint(char*str)
1100 {
1101     if(strchr(str, '('))
1102         return 1;
1103     else
1104         return 0;
1105 }
1106
1107 SPOINT parsePoint(char*str)
1108 {
1109     SPOINT p;
1110     char tmp[80];
1111     int l = strlen(str);
1112     char*comma = strchr(str, ',');
1113     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1114         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1115     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1116     p.x = parseTwip(tmp);
1117     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1118     p.y = parseTwip(tmp);
1119     return p;
1120 }
1121
1122 int parseColor2(char*str, RGBA*color)
1123 {
1124     int l = strlen(str);
1125     int r,g,b,a;
1126     int t;
1127
1128     struct {unsigned char r,g,b;char*name;} colors[] =
1129     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1130     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1131     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1132     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1133     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1134     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1135     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1136     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1137     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1138     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1139     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1140     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1141
1142     a=255;r=g=b=0;
1143
1144     if(str[0]=='#' && (l==7 || l==9)) {
1145         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1146             return 0;
1147         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1148             return 0;
1149         color->r = r; color->g = g; color->b = b; color->a = a;
1150         return 1;
1151     }
1152     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1153         if(!strcmp(str, colors[t].name)) {
1154             r = colors[t].r;
1155             g = colors[t].g;
1156             b = colors[t].b;
1157             a = 255;
1158             color->r = r; color->g = g; color->b = b; color->a = a;
1159             return 1;
1160         }
1161     return 0;
1162
1163 }
1164 RGBA parseColor(char*str)
1165 {
1166     RGBA c;
1167     if(!parseColor2(str, &c))
1168         syntaxerror("Expression '%s' is not a color", str);
1169     return c;
1170 }
1171
1172 typedef struct _muladd {
1173     S16 mul;
1174     S16 add;
1175 } MULADD;
1176
1177 MULADD parseMulAdd(char*str)
1178 {
1179     float add, mul;
1180     char* str2 = (char*)malloc(strlen(str)+5);
1181     int i;
1182     MULADD m;
1183     strcpy(str2, str);
1184     strcat(str2, " 0");
1185     add = 0;
1186     mul = 1.0;
1187     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1188     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1189     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1190     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1191     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1192     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1193     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1194     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1195     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1196     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1197     else {
1198         syntaxerror("'%s' is not a valid color transform expression", str);
1199     }
1200     m.add = (int)(add*256);
1201     m.mul = (int)(mul*256);
1202     free(str2);
1203     return m;
1204 }
1205
1206 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1207 {
1208     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1209     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1210     MULADD r;
1211     if(a<-32768) a=-32768;
1212     if(a>32767) a=32767;
1213     if(m<-32768) m=-32768;
1214     if(m>32767) m=32767;
1215     r.add = a;
1216     r.mul = (int)m;
1217     return r;
1218 }
1219
1220 float parsePercent(char*str)
1221 {
1222     int l = strlen(str);
1223     if(!l)
1224         return 1.0;
1225     if(str[l-1]=='%') {
1226         return atoi(str)/100.0;
1227     }
1228     syntaxerror("Expression '%s' is not a percentage", str);
1229     return 0;
1230 }
1231 int isPercent(char*str)
1232 {
1233     return str[strlen(str)-1]=='%';
1234 }
1235 int parseNewSize(char*str, int size)
1236 {
1237     if(isPercent(str))
1238         return parsePercent(str)*size;
1239     else
1240         return (int)(atof(str)*20);
1241 }
1242
1243 int isColor(char*str)
1244 {
1245     RGBA c;
1246     return parseColor2(str, &c);
1247 }
1248
1249 static char* lu(map_t* args, char*name)
1250 {
1251     char* value = map_lookup(args, name);
1252     if(!value) {
1253         map_dump(args, stdout, "");
1254         syntaxerror("internal error 2: value %s should be set", name);
1255     }
1256     return value;
1257 }
1258
1259 static int c_swf(map_t*args) 
1260 {
1261     char* name = lu(args, "name");
1262     char* compressstr = lu(args, "compress");
1263     SRECT bbox = parseBox(lu(args, "bbox"));
1264     int version = parseInt(lu(args, "version"));
1265     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1266     int compress = 0;
1267     RGBA color = parseColor(lu(args, "background"));
1268     if(!strcmp(name, "!default!") || override_outputname)
1269         name = outputname;
1270     
1271     if(!strcmp(compressstr, "default"))
1272         compress = version==6;
1273     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1274         compress = 1;
1275     else if(!strcmp(compressstr, "no"))
1276         compress = 0;
1277     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1278
1279     s_swf(name, bbox, version, fps, compress, color);
1280     return 0;
1281 }
1282 int isRelative(char*str)
1283 {
1284     return !strncmp(str, "<plus>", 6) ||
1285            !strncmp(str, "<minus>", 7);
1286 }
1287 char* getOffset(char*str)
1288 {
1289     if(!strncmp(str, "<plus>", 6))
1290         return str+6;
1291     if(!strncmp(str, "<minus>", 7))
1292         return str+7;
1293     syntaxerror("internal error (347)");
1294     return 0;
1295 }
1296 int getSign(char*str)
1297 {
1298     if(!strncmp(str, "<plus>", 6))
1299         return 1;
1300     if(!strncmp(str, "<minus>", 7))
1301         return -1;
1302     syntaxerror("internal error (348)");
1303     return 0;
1304 }
1305 static dictionary_t points;
1306 static mem_t mpoints;
1307 int points_initialized = 0;
1308
1309 SPOINT getPoint(SRECT r, char*name)
1310 {
1311     int l=0;
1312     if(!strcmp(name, "center")) {
1313         SPOINT p;
1314         p.x = (r.xmin + r.xmax)/2;
1315         p.y = (r.ymin + r.ymax)/2;
1316         return p;
1317     }
1318
1319     if(points_initialized)
1320         l = (int)dictionary_lookup(&points, name);
1321     if(l==0) {
1322         syntaxerror("Invalid point: \"%s\".", name);
1323     }
1324     l--;
1325     return *(SPOINT*)&mpoints.buffer[l];
1326 }
1327 static int c_gradient(map_t*args) 
1328 {
1329     return 0;
1330 }
1331 static int c_point(map_t*args) 
1332 {
1333     char*name = lu(args, "name");
1334     int pos;
1335     string_t s1;
1336     SPOINT p;
1337     if(!points_initialized) {
1338         dictionary_init(&points);
1339         mem_init(&mpoints);
1340         points_initialized = 1;
1341     }
1342     p.x = parseTwip(lu(args, "x"));
1343     p.y = parseTwip(lu(args, "y"));
1344     pos = mem_put(&mpoints, &p, sizeof(p));
1345     string_set(&s1, name);
1346     pos++;
1347     dictionary_put(&points, s1, (void*)pos);
1348     return 0;
1349 }
1350 static int c_play(map_t*args) 
1351 {
1352     char*name = lu(args, "sound");
1353     char*loop = lu(args, "loop");
1354     char*nomultiple = lu(args, "nomultiple");
1355     int nm = 0;
1356     if(!strcmp(nomultiple, "nomultiple"))
1357         nm = 1;
1358     else
1359         nm = parseInt(nomultiple);
1360
1361     s_playsound(name, parseInt(loop), nm, 0);
1362     return 0;
1363 }
1364
1365 static int c_stop(map_t*args) 
1366 {
1367     char*name = lu(args, "sound");
1368     s_playsound(name, 0,0,1);
1369     return 0;
1370 }
1371
1372 static int c_placement(map_t*args, int type)
1373 {
1374     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1375     char*character = 0;
1376
1377     char* luminancestr = lu(args, "luminance");
1378     char* scalestr = lu(args, "scale");
1379     char* scalexstr = lu(args, "scalex");
1380     char* scaleystr = lu(args, "scaley");
1381     char* rotatestr = lu(args, "rotate");
1382     char* shearstr = lu(args, "shear");
1383     char* xstr="", *pivotstr="";
1384     char* ystr="", *anglestr="";
1385     char*above = lu(args, "above"); /*FIXME*/
1386     char*below = lu(args, "below");
1387     char* rstr = lu(args, "red");
1388     char* gstr = lu(args, "green");
1389     char* bstr = lu(args, "blue");
1390     char* astr = lu(args, "alpha");
1391     char* pinstr = lu(args, "pin");
1392     MULADD r,g,b,a;
1393     float oldwidth;
1394     float oldheight;
1395     SRECT oldbbox;
1396     MULADD luminance;
1397     parameters_t p;
1398
1399     if(type==5) {
1400         pivotstr = lu(args, "pivot");
1401         anglestr = lu(args, "angle");
1402     } else {
1403         xstr = lu(args, "x");
1404         ystr = lu(args, "y");
1405     }
1406     if(luminancestr[0])
1407         luminance = parseMulAdd(luminancestr);
1408     else {
1409         luminance.add = 0;
1410         luminance.mul = 256;
1411     }
1412
1413     if(scalestr[0]) {
1414         if(scalexstr[0]||scaleystr[0])
1415             syntaxerror("scalex/scaley and scale cannot both be set");
1416         scalexstr = scaleystr = scalestr;
1417     }
1418     
1419     if(type == 0 || type == 4)  {
1420         // put or startclip
1421         character = lu(args, "character");
1422         parameters_clear(&p);
1423     } else {
1424         p = s_getParameters(instance);
1425     }
1426
1427     /* x,y position */
1428     if(xstr[0]) {
1429         if(isRelative(xstr)) {
1430             if(type == 0 || type == 4)
1431                 syntaxerror("relative x values not allowed for initial put or startclip");
1432             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1433         } else {
1434             p.x = parseTwip(xstr);
1435         }
1436     }
1437     if(ystr[0]) {
1438         if(isRelative(ystr)) {
1439             if(type == 0 || type == 4)
1440                 syntaxerror("relative y values not allowed for initial put or startclip");
1441             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1442         } else {
1443             p.y = parseTwip(ystr);
1444         }
1445     }
1446
1447     /* scale, scalex, scaley */
1448     if(character) {
1449         oldbbox = s_getCharBBox(character);
1450     } else {
1451         oldbbox = s_getInstanceBBox(instance);
1452     }
1453     oldwidth = oldbbox.xmax - oldbbox.xmin;
1454     oldheight = oldbbox.ymax - oldbbox.ymin;
1455     if(scalexstr[0]) {
1456         if(oldwidth==0) p.scalex = 1.0;
1457         else {      
1458             if(scalexstr[0])
1459                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1460         }
1461     }
1462     if(scaleystr[0]) {
1463         if(oldheight==0) p.scaley = 1.0;
1464         else {
1465             if(scaleystr[0])
1466                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1467         }
1468     }
1469    
1470     /* rotation */
1471     if(rotatestr[0]) {
1472         if(isRelative(rotatestr)) {
1473             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1474         } else {
1475             p.rotate = parseFloat(rotatestr);
1476         }
1477     }
1478
1479     /* shearing */
1480     if(shearstr[0]) {
1481         if(isRelative(shearstr)) {
1482             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1483         } else {
1484             p.shear = parseFloat(shearstr);
1485         }
1486     }
1487
1488     if(pivotstr[0]) {
1489         if(isPoint(pivotstr)) 
1490             p.pivot = parsePoint(pivotstr);
1491         else 
1492             p.pivot = getPoint(oldbbox, pivotstr);
1493     }
1494     if(pinstr[0]) {
1495         if(isPoint(pinstr))
1496             p.pin = parsePoint(pinstr);
1497         else
1498             p.pin = getPoint(oldbbox, pinstr);
1499     }
1500         
1501     /* color transform */
1502
1503     if(rstr[0] || luminancestr[0]) {
1504         MULADD r;
1505         if(rstr[0])
1506             r = parseMulAdd(rstr);
1507         else {
1508             r.add = p.cxform.r0;
1509             r.mul = p.cxform.r1;
1510         }
1511         r = mergeMulAdd(r, luminance);
1512         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1513     }
1514     if(gstr[0] || luminancestr[0]) {
1515         MULADD g;
1516         if(gstr[0])
1517             g = parseMulAdd(gstr);
1518         else {
1519             g.add = p.cxform.g0;
1520             g.mul = p.cxform.g1;
1521         }
1522         g = mergeMulAdd(g, luminance);
1523         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1524     }
1525     if(bstr[0] || luminancestr[0]) {
1526         MULADD b;
1527         if(bstr[0])
1528             b = parseMulAdd(bstr);
1529         else {
1530             b.add = p.cxform.b0;
1531             b.mul = p.cxform.b1;
1532         }
1533         b = mergeMulAdd(b, luminance);
1534         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1535     }
1536     if(astr[0]) {
1537         MULADD a = parseMulAdd(astr);
1538         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1539     }
1540
1541     if(type == 0)
1542         s_put(instance, character, p);
1543     if(type == 1)
1544         s_change(instance, p);
1545     if(type == 2)
1546         s_qchange(instance, p);
1547     if(type == 3)
1548         s_jump(instance, p);
1549     if(type == 4)
1550         s_startclip(instance, character, p);
1551     return 0;
1552 }
1553 static int c_put(map_t*args) 
1554 {
1555     c_placement(args, 0);
1556     return 0;
1557 }
1558 static int c_change(map_t*args) 
1559 {
1560     c_placement(args, 1);
1561     return 0;
1562 }
1563 static int c_qchange(map_t*args) 
1564 {
1565     c_placement(args, 2);
1566     return 0;
1567 }
1568 static int c_arcchange(map_t*args) 
1569 {
1570     c_placement(args, 2);
1571     return 0;
1572 }
1573 static int c_jump(map_t*args) 
1574 {
1575     c_placement(args, 3);
1576     return 0;
1577 }
1578 static int c_startclip(map_t*args) 
1579 {
1580     c_placement(args, 4);
1581     return 0;
1582 }
1583 static int c_del(map_t*args) 
1584 {
1585     char*instance = lu(args, "name");
1586     s_delinstance(instance);
1587     return 0;
1588 }
1589 static int c_end(map_t*args) 
1590 {
1591     s_end();
1592     return 0;
1593 }
1594 static int c_sprite(map_t*args) 
1595 {
1596     char* name = lu(args, "name");
1597     s_sprite(name);
1598     return 0;
1599 }
1600 static int c_frame(map_t*args) 
1601 {
1602     char*framestr = lu(args, "n");
1603     int frame;
1604     if(isRelative(framestr)) {
1605         frame = s_getframe();
1606         if(getSign(framestr)<0)
1607             syntaxerror("relative frame expressions must be positive");
1608         frame += parseInt(getOffset(framestr));
1609     }
1610     else {
1611         frame = parseInt(framestr);
1612         if(s_getframe() >= frame
1613                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1614             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1615     }
1616     s_frame(frame);
1617     return 0;
1618 }
1619 static int c_primitive(map_t*args) 
1620 {
1621     char*name = lu(args, "name");
1622     char*command = lu(args, "commandname");
1623     int width=0, height=0, r=0;
1624     int linewidth = parseTwip(lu(args, "line"));
1625     char*colorstr = lu(args, "color");
1626     RGBA color = parseColor(colorstr);
1627     char*fillstr = lu(args, "fill");
1628     int dofill = 1;
1629     int type=0;
1630     char* font;
1631     char* text;
1632     RGBA fill;
1633     if(!strcmp(command, "circle"))
1634         type = 1;
1635     else if(!strcmp(command, "textshape"))
1636         type = 2;
1637    
1638     if(type==0) {
1639         width = parseTwip(lu(args, "width"));
1640         height = parseTwip(lu(args, "height"));
1641     } else if (type==1) {
1642         r = parseTwip(lu(args, "r"));
1643     } else if (type==2) {
1644         text = lu(args, "text");
1645         font = lu(args, "font");
1646     }
1647
1648     if(!strcmp(fillstr, "fill"))
1649         fillstr = colorstr;
1650     if(!strcmp(fillstr, "none"))
1651         dofill = 0;
1652     if(width<0 || height<0 || linewidth<0 || r<0)
1653         syntaxerror("values width, height, line, r must be positive");
1654     if(!dofill || isColor(fillstr)) {
1655         if(dofill) 
1656             fill = parseColor(fillstr);
1657     } else {
1658         /* FIXME - texture fill */
1659         fill.r = fill.g = 0;
1660         fill.b = fill.a = 255;
1661         warning("texture fill not supported yet. Filling with black.");
1662     }
1663     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1664     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1665     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1666     return 0;
1667 }
1668
1669 static int c_shape(map_t*args) 
1670 {
1671     char*name = lu(args, "name");
1672     char*filename = lu(args, "filename");
1673     s_shape(name, filename);
1674     return 0;
1675 }
1676
1677 static int c_font(map_t*args) 
1678 {
1679     char*name = lu(args, "name");
1680     char*filename = lu(args, "filename");
1681     s_font(name, filename);
1682     return 0;
1683 }
1684
1685 static int c_sound(map_t*args) 
1686 {
1687     char*name = lu(args, "name");
1688     char*filename = lu(args, "filename");
1689     s_sound(name, filename);
1690     return 0;
1691 }
1692
1693 static int c_text(map_t*args) 
1694 {
1695     char*name = lu(args, "name");
1696     char*text = lu(args, "text");
1697     char*font = lu(args, "font");
1698     float size = parsePercent(lu(args, "size"));
1699     RGBA color = parseColor(lu(args, "color"));
1700     s_text(name, font, text, (int)(size*100), color);
1701     return 0;
1702 }
1703
1704 static int c_soundtrack(map_t*args) 
1705 {
1706     return 0;
1707 }
1708
1709 int fakechar(map_t*args)
1710 {
1711     char*name = lu(args, "name");
1712     s_box(name, 0, 0, black, 20, black, 0);
1713     return 0;
1714 }
1715
1716 static int c_egon(map_t*args) {return fakechar(args);}
1717 static int c_button(map_t*args) {return fakechar(args);}
1718 static int c_edittext(map_t*args) {return fakechar(args);}
1719
1720 static int c_morphshape(map_t*args) {return fakechar(args);}
1721 static int c_image(map_t*args) {return fakechar(args);}
1722 static int c_movie(map_t*args) {return fakechar(args);}
1723
1724 static int c_buttonsounds(map_t*args) {return 0;}
1725 static int c_buttonput(map_t*args) {return 0;}
1726 static int c_texture(map_t*args) {return 0;}
1727 static int c_action(map_t*args) {return 0;}
1728
1729 static struct {
1730     char*command;
1731     command_func_t* func;
1732     char*arguments;
1733 } arguments[] =
1734 {{"swf", c_swf, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
1735  {"frame", c_frame, "n=<plus>1"},
1736
1737     // "import" type stuff
1738  {"shape", c_shape, "name filename"},
1739  {"morphshape", c_morphshape, "name start end"},
1740  {"jpeg", c_image, "name filename quality=80%"},
1741  {"png", c_image, "name filename"},
1742  {"movie", c_movie, "name filename"},
1743  {"sound", c_sound, "name filename"},
1744  {"font", c_font, "name filename"},
1745  {"soundtrack", c_soundtrack, "filename"},
1746
1747     // generators of primitives
1748
1749  {"point", c_point, "name x=0 y=0"},
1750  {"gradient", c_gradient, "name"},
1751
1752     // character generators
1753  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1754  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1755  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1756  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1757  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1758  {"text", c_text, "name text font size=100% color=white"},
1759  {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
1760  
1761  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1762
1763     // control tags
1764  {"play", c_play, "sound loop=0 @nomultiple=0"},
1765  {"stop", c_stop, "sound"},
1766
1767     // object placement tags
1768  {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1769  {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1770  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1771  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1772  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1773  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1774  {"del", c_del, "name"},
1775     // virtual object placement
1776  {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% shear=0 rotate=0 above= below="},
1777  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1778
1779     // commands which start a block
1780 //startclip (see above)
1781  {"sprite", c_sprite, "name"},
1782  {"action", c_action, ""},
1783
1784  {"end", c_end, ""}
1785 };
1786
1787
1788 static map_t parseArguments(char*command, char*pattern)
1789 {
1790     char*x;
1791     char*d,*e;
1792    
1793     string_t name[64];
1794     string_t value[64];
1795     int set[64];
1796     int isboolean[64];
1797     int pos;
1798     int len;
1799     int t;
1800     string_t t1,t2;
1801     map_t result;
1802     map_init(&result);
1803
1804     string_set(&t1, "commandname");
1805     string_set(&t2, command);
1806     map_put(&result, t1, t2);
1807
1808     if(!pattern || !*pattern)
1809         return result;
1810
1811     x = pattern;
1812
1813     pos = 0;
1814
1815     if(!strncmp("<i> ", x, 3)) {
1816         readToken();
1817         if(type == COMMAND || type == LABEL) {
1818             pushBack();
1819             syntaxerror("character name expected");
1820         }
1821         name[pos].str = "instance";
1822         name[pos].len = 8;
1823         value[pos].str = text;
1824         value[pos].len = strlen(text);
1825         set[pos] = 1;
1826         pos++;
1827
1828         if(type == ASSIGNMENT)
1829             readToken();
1830
1831         name[pos].str = "character";
1832         name[pos].len = 9;
1833         value[pos].str = text;
1834         value[pos].len = strlen(text);
1835         set[pos] = 1;
1836         pos++;
1837
1838         x+=4;
1839     }
1840
1841     while(*x) {
1842         isboolean[pos] = (x[0] =='@');
1843         if(isboolean[pos])
1844             x++;
1845
1846         d = strchr(x, ' ');
1847         e = strchr(x, '=');
1848         if(!d)
1849             d=&x[strlen(x)];
1850         set[pos] = 0;
1851
1852         if(!e || d<e) { 
1853             // no default
1854             name[pos].str = x;
1855             name[pos].len = d-x;
1856             value[pos].str = 0;
1857             value[pos].len = 0;
1858         } else {
1859             name[pos].str = x;
1860             name[pos].len = e-x;
1861             value[pos].str = e+1;
1862             value[pos].len = d-e-1;
1863         }
1864         pos++;
1865         if(!*d) break;
1866         x=d+1;
1867     }
1868     len = pos;
1869
1870 /*    for(t=0;t<len;t++) {
1871         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
1872                 isboolean[t]?"(boolean)":"");
1873     }*/
1874
1875     while(1) {
1876         readToken();
1877         if(type == LABEL || type == COMMAND) {
1878             pushBack();
1879             break;
1880         }
1881
1882         // first, search for boolean arguments
1883         for(pos=0;pos<len;pos++)
1884         {
1885             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1886                 set[pos] = 1;
1887                 if(type == ASSIGNMENT)
1888                     readToken();
1889                 value[pos].str = text;
1890                 value[pos].len = strlen(text);
1891                 /*printf("setting boolean parameter %s (to %s)\n",
1892                         strdup_n(name[pos], namelen[pos]),
1893                         strdup_n(value[pos], valuelen[pos]));*/
1894                 break;
1895             }
1896         }
1897
1898         // second, search for normal arguments
1899         if(pos==len)
1900         for(pos=0;pos<len;pos++)
1901         {
1902             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1903                (type != ASSIGNMENT && !set[pos])) {
1904                 if(set[pos]) {
1905                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
1906                 }
1907                 if(type == ASSIGNMENT)
1908                     readToken();
1909                 set[pos] = 1;
1910                 value[pos].str = text;
1911                 value[pos].len = strlen(text);
1912 #if 0//def DEBUG
1913                 printf("setting parameter %s (to %s)\n",
1914                         strdup_n(name[pos].str, name[pos].len),
1915                         strdup_n(value[pos].str, value[pos].len));
1916 #endif
1917                 break;
1918             }
1919         }
1920         if(pos==len) {
1921             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1922         }
1923     }
1924 #if 0//def DEBUG
1925     for(t=0;t<len;t++) {
1926         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
1927     }
1928 #endif
1929     for(t=0;t<len;t++) {
1930         if(value[t].str && value[t].str[0] == '*') {
1931             //relative default- take value from some other parameter
1932             int s;
1933             for(s=0;s<len;s++) {
1934                 if(value[s].len == value[t].len-1 &&
1935                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1936                     value[t].str = value[s].str;
1937             }
1938         }
1939         if(value[t].str == 0) {
1940             pushBack();
1941             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
1942         }
1943     }
1944
1945     /* ok, now construct the dictionary from the parameters */
1946
1947     for(t=0;t<len;t++) 
1948     {
1949         map_put(&result, name[t], value[t]);
1950     }
1951     return result;
1952 }
1953 static void parseArgumentsForCommand(char*command)
1954 {
1955     int t;
1956     map_t args;
1957     int nr = -1;
1958     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1959         if(!strcmp(arguments[t].command, command)) {
1960             args = parseArguments(command, arguments[t].arguments);
1961             nr = t;
1962         }
1963     }
1964     if(nr<0)
1965         syntaxerror("command %s not known", command);
1966
1967 #ifdef DEBUG
1968     printf(".%s\n", command);fflush(stdout);
1969     map_dump(&args, stdout, "\t");fflush(stdout);
1970 #endif
1971
1972     (*arguments[nr].func)(&args);
1973
1974     if(!strcmp(command, "button") ||
1975        !strcmp(command, "action")) {
1976         while(1) {
1977             readToken();
1978             if(type == COMMAND) {
1979                 if(!strcmp(text, "end"))
1980                     break;
1981                 else {
1982                     pushBack();
1983                     break;
1984                 }
1985             }
1986         }
1987     }
1988
1989     map_clear(&args);
1990     return;
1991 }
1992
1993 int main (int argc,char ** argv)
1994
1995     int t;
1996     processargs(argc, argv);
1997     initLog(0,-1,0,0,-1,verbose);
1998
1999     if(!filename) {
2000         args_callback_usage(argv[0]);
2001         exit(1);
2002     }
2003     file = generateTokens(filename);
2004     if(!file) {
2005         printf("parser returned error.\n");
2006         return 1;
2007     }
2008     pos=0;
2009
2010     t=0;
2011
2012     while(!noMoreTokens()) {
2013         readToken();
2014         if(type != COMMAND)
2015             syntaxerror("command expected");
2016         parseArgumentsForCommand(text);
2017     }
2018
2019     s_close();
2020     freeTokens(file);
2021
2022     return 0;
2023 }
2024