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