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