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