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