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