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