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