49897efa2515506ac6b242433d4e146dba9987cf
[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     SWFFONT* font;
815     font = swf_LoadFont(filename);
816    
817     if(font == 0) {
818         warning("Couldn't open font file \"%s\"", filename);
819         font = (SWFFONT*)malloc(sizeof(SWFFONT));
820         memset(font, 0, sizeof(SWFFONT));
821         dictionary_put2(&fonts, name, font);
822         return;
823     }
824
825     if(0)
826     {
827         /* fix the layout. Only needed for old fonts */
828         int t;
829         for(t=0;t<font->numchars;t++) {
830             font->glyph[t].advance = 0;
831         }
832         font->layout = 0;
833         swf_FontCreateLayout(font);
834     }
835
836     font->id = id;
837     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
838     swf_FontSetDefine2(tag, font);
839     incrementid();
840
841     if(dictionary_lookup(&fonts, name))
842         syntaxerror("font %s defined twice", name);
843     dictionary_put2(&fonts, name, font);
844 }
845
846
847
848 typedef struct _sound_t
849 {
850     U16 id;
851     TAG*tag;
852 } sound_t;
853
854 void s_sound(char*name, char*filename)
855 {
856     struct WAV wav, wav2;
857     sound_t* sound;
858     U16*samples;
859     int numsamples;
860
861     if(!readWAV(filename, &wav)) {
862         warning("Couldn't read wav file \"%s\"", filename);
863         samples = 0;
864         numsamples = 0;
865     } else {
866         convertWAV2mono(&wav, &wav2, 44100);
867         samples = (U16*)wav2.data;
868         numsamples = wav2.size/2;
869         free(wav.data);
870     }
871
872     tag = swf_InsertTag(tag, ST_DEFINESOUND);
873     swf_SetU16(tag, id); //id
874     swf_SetSoundDefine(tag, samples, numsamples);
875    
876     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
877     sound->tag = tag;
878     sound->id = id;
879
880     if(dictionary_lookup(&sounds, name))
881         syntaxerror("sound %s defined twice", name);
882     dictionary_put2(&sounds, name, sound);
883     
884     incrementid();
885
886     if(samples)
887         free(samples);
888 }
889
890 static char* gradient_getToken(const char**p)
891 {
892     const char*start;
893     char*result;
894     while(**p && strchr(" \t\n\r", **p)) {
895         (*p)++;
896     } 
897     start = *p;
898     while(**p && !strchr(" \t\n\r", **p)) {
899         (*p)++;
900     }
901     result = malloc((*p)-start+1);
902     memcpy(result,start,(*p)-start+1);
903     result[(*p)-start] = 0;
904     return result;
905 }
906
907 float parsePercent(char*str);
908 RGBA parseColor(char*str);
909
910 GRADIENT parseGradient(const char*str)
911 {
912     GRADIENT gradient;
913     const char* p = str;
914     memset(&gradient, 0, sizeof(GRADIENT));
915     while(*p) {
916         char*posstr,*colorstr;
917         float pos;
918         RGBA color;
919         posstr = gradient_getToken(&p);
920         if(!*posstr)
921             break;
922         pos = parsePercent(posstr);
923         if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
924         colorstr = gradient_getToken(&p);
925         color = parseColor(colorstr);
926         if(gradient.num == sizeof(gradient.ratios)/sizeof(gradient.ratios[0])) {
927             warning("gradient record too big- max size is 8, rest ignored");
928             break;
929         }
930         gradient.ratios[gradient.num] = (int)(pos*255.0);
931         gradient.rgba[gradient.num] = color;
932         gradient.num++;
933         free(posstr);
934         free(colorstr);
935     }
936     return gradient;
937 }
938
939 void s_gradient(char*name, const char*text, int radial)
940 {
941     gradient_t* gradient;
942     gradient = malloc(sizeof(gradient_t));
943     memset(gradient, 0, sizeof(gradient_t));
944     gradient->gradient = parseGradient(text);
945     gradient->radial = radial;
946
947     if(dictionary_lookup(&gradients, name))
948         syntaxerror("gradient %s defined twice", name);
949     dictionary_put2(&gradients, name, gradient);
950 }
951
952 void s_action(const char*text)
953 {
954     ActionTAG* a = 0;
955     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
956
957     tag = swf_InsertTag(tag, ST_DOACTION);
958
959     swf_ActionSet(tag, a);
960
961     swf_ActionFree(a);
962 }
963
964 void s_outline(char*name, char*format, char*source)
965 {
966     outline_t* outline;
967
968     drawer_t draw;
969     SHAPE* shape;
970     SHAPE2* shape2;
971     SRECT bounds;
972     
973     swf_Shape11DrawerInit(&draw, 0);
974     draw_string(&draw, source);
975     draw.finish(&draw);
976     shape = swf_ShapeDrawerToShape(&draw);
977     //shape2 = swf_ShapeToShape2(shape);
978     //bounds = swf_GetShapeBoundingBox(shape2);
979     //swf_Shape2Free(shape2);
980     bounds = swf_ShapeDrawerGetBBox(&draw);
981     draw.dealloc(&draw);
982     
983     outline = (outline_t*)malloc(sizeof(outline_t));
984     memset(outline, 0, sizeof(outline_t));
985     outline->shape = shape;
986     outline->bbox = bounds;
987     
988     if(dictionary_lookup(&outlines, name))
989         syntaxerror("outline %s defined twice", name);
990     dictionary_put2(&outlines, name, outline);
991 }
992
993 void s_playsound(char*name, int loops, int nomultiple, int stop)
994 {
995     sound_t* sound = dictionary_lookup(&sounds, name);
996     SOUNDINFO info;
997     if(!sound)
998         syntaxerror("Don't know anything about sound \"%s\"", name);
999
1000     tag = swf_InsertTag(tag, ST_STARTSOUND);
1001     swf_SetU16(tag, sound->id); //id
1002     memset(&info, 0, sizeof(info));
1003     info.stop = stop;
1004     info.loops = loops;
1005     info.nomultiple = nomultiple;
1006     swf_SetSoundInfo(tag, &info);
1007 }
1008
1009 void s_includeswf(char*name, char*filename)
1010 {
1011     int f;
1012     SWF swf;
1013     TAG* ftag;
1014     SRECT r;
1015     TAG* s;
1016     int level = 0;
1017     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1018     f = open(filename,O_RDONLY);
1019     if (f<0) { 
1020         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1021         s_box(name, 0, 0, black, 20, 0);
1022         return;
1023     }
1024     if (swf_ReadSWF(f,&swf)<0) { 
1025         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1026         s_box(name, 0, 0, black, 20, 0);
1027         return;
1028     }
1029     close(f);
1030
1031     /* FIXME: The following sets the bounding Box for the character. 
1032               It is wrong for two reasons:
1033               a) It may be too small (in case objects in the movie clip at the borders)
1034               b) it may be too big (because the poor movie never got autocropped)
1035     */
1036     r = swf.movieSize;
1037     
1038     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1039     swf_SetU16(tag, id);
1040     swf_SetU16(tag, 0);
1041
1042     swf_Relocate(&swf, idmap);
1043
1044     ftag = swf.firstTag;
1045     level = 1;
1046     while(ftag) {
1047         int t;
1048         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1049             if(cutout[t] == ftag->id) {
1050                 ftag = ftag->next;
1051                 continue;
1052             }
1053         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1054             level++;
1055         if(ftag->id == ST_END)
1056             level--;
1057         if(!level)
1058             break;
1059         /* We simply dump all tags right after the sprite
1060            header, relying on the fact that swf_OptimizeTagOrder() will
1061            sort things out for us later. 
1062            We also rely on the fact that the imported SWF is well-formed.
1063          */
1064         tag = swf_InsertTag(tag, ftag->id);
1065         swf_SetBlock(tag, ftag->data, ftag->len);
1066         ftag = ftag->next;
1067     }
1068     if(!ftag)
1069         syntaxerror("Included file %s contains errors", filename);
1070     tag = swf_InsertTag(tag, ST_END);
1071
1072     swf_FreeTags(&swf);
1073
1074     s_addcharacter(name, id, tag, r);
1075     incrementid();
1076 }
1077 SRECT s_getCharBBox(char*name)
1078 {
1079     character_t* c = dictionary_lookup(&characters, name);
1080     if(!c) syntaxerror("character '%s' unknown(2)", name);
1081     return c->size;
1082 }
1083 SRECT s_getInstanceBBox(char*name)
1084 {
1085     instance_t * i = dictionary_lookup(&instances, name);
1086     character_t * c;
1087     if(!i) syntaxerror("instance '%s' unknown(4)", name);
1088     c = i->character;
1089     if(!c) syntaxerror("internal error(5)");
1090     return c->size;
1091 }
1092 parameters_t s_getParameters(char*name)
1093 {
1094     instance_t * i = dictionary_lookup(&instances, name);
1095     if(!i) syntaxerror("instance '%s' unknown(10)", name);
1096     return i->parameters;
1097 }
1098 void s_startclip(char*instance, char*character, parameters_t p)
1099 {
1100     character_t* c = dictionary_lookup(&characters, character);
1101     instance_t* i;
1102     MATRIX m;
1103     if(!c) {
1104         syntaxerror("character %s not known", character);
1105     }
1106     i = s_addinstance(instance, c, currentdepth);
1107     i->parameters = p;
1108     m = s_instancepos(i, &p);
1109     
1110     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1111     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1112     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1113     i->lastTag = tag;
1114     i->lastFrame= currentframe;
1115
1116     stack[stackpos].tag = tag;
1117     stack[stackpos].type = 2;
1118     stackpos++;
1119
1120     currentdepth++;
1121 }
1122 void s_endClip()
1123 {
1124     SWFPLACEOBJECT p;
1125     stackpos--;
1126     swf_SetTagPos(stack[stackpos].tag, 0);
1127     swf_GetPlaceObject(stack[stackpos].tag, &p);
1128     p.clipdepth = currentdepth;
1129     swf_ClearTag(stack[stackpos].tag);
1130     swf_SetPlaceObject(stack[stackpos].tag, &p);
1131     currentdepth++;
1132 }
1133
1134 void s_put(char*instance, char*character, parameters_t p)
1135 {
1136     character_t* c = dictionary_lookup(&characters, character);
1137     instance_t* i;
1138     MATRIX m;
1139     if(!c) {
1140         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1141     }
1142     
1143     i = s_addinstance(instance, c, currentdepth);
1144     i->parameters = p;
1145     m = s_instancepos(i, &p);
1146     
1147     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1148     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1149     i->lastTag = tag;
1150     i->lastFrame = currentframe;
1151     currentdepth++;
1152 }
1153
1154 void s_jump(char*instance, parameters_t p)
1155 {
1156     instance_t* i = dictionary_lookup(&instances, instance);
1157     MATRIX m;
1158     if(!i) {
1159         syntaxerror("instance %s not known", instance);
1160     }
1161
1162     i->parameters = p;
1163     m = s_instancepos(i, &p);
1164
1165     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1166     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1167     i->lastTag = tag;
1168     i->lastFrame = currentframe;
1169 }
1170
1171 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1172 {
1173     parameters_t p;
1174     float ratio;
1175     if(num==0 || num==1)
1176         return *p1;
1177     ratio = (float)pos/(float)num;
1178     
1179     p.x = (p2->x-p1->x)*ratio + p1->x;
1180     p.y = (p2->y-p1->y)*ratio + p1->y;
1181     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1182     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1183     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1184     p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1185
1186     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1187     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1188     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1189     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1190
1191     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1192     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1193     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1194     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1195
1196     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1197     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1198     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1199     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1200     return p;
1201 }
1202
1203 void s_change(char*instance, parameters_t p2)
1204 {
1205     instance_t* i = dictionary_lookup(&instances, instance);
1206     MATRIX m;
1207     parameters_t p1;
1208     TAG*t;
1209     int frame, allframes;
1210     if(!i) {
1211         syntaxerror("instance %s not known", instance);
1212     }
1213     p1 = i->parameters;
1214     
1215     allframes = currentframe - i->lastFrame - 1;
1216     if(allframes < 0) {
1217         warning(".change ignored. can only .put/.change an object once per frame.");
1218         return;
1219     }
1220     
1221     m = s_instancepos(i, &p2);
1222     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1223     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1224     i->parameters = p2;
1225
1226     /* o.k., we got the start and end point set. Now iterate though all the
1227        tags in between, inserting object changes after each new frame */
1228     t = i->lastTag;
1229     i->lastTag = tag;
1230     if(!t) syntaxerror("internal error(6)");
1231     frame = 0;
1232     while(frame < allframes) {
1233         if(t->id == ST_SHOWFRAME) {
1234             parameters_t p;
1235             MATRIX m;
1236             TAG*lt;
1237             frame ++;
1238             p = s_interpolate(&p1, &p2, frame, allframes);
1239             m = s_instancepos(i, &p); //needed?
1240             lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1241             i->lastFrame = currentframe;
1242             swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1243             t = lt;
1244             if(frame == allframes)
1245                 break;
1246         }
1247         t = t->next;
1248         if(!t) 
1249             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1250     }
1251 }
1252
1253 void s_delinstance(char*instance)
1254 {
1255     instance_t* i = dictionary_lookup(&instances, instance);
1256     if(!i) {
1257         syntaxerror("instance %s not known", instance);
1258     }
1259     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1260     swf_SetU16(tag, i->depth);
1261     dictionary_del(&instances, instance);
1262 }
1263
1264 void s_qchange(char*instance, parameters_t p)
1265 {
1266 }
1267
1268 void s_end()
1269 {
1270     if(!stackpos)
1271         syntaxerror(".end unexpected");
1272     if(stack[stackpos-1].type == 0)
1273         s_endSWF();
1274     else if(stack[stackpos-1].type == 1)
1275         s_endSprite();
1276     else if(stack[stackpos-1].type == 2)
1277         s_endClip();
1278     else syntaxerror("internal error 1");
1279 }
1280
1281 // ------------------------------------------------------------------------
1282
1283 typedef int command_func_t(map_t*args);
1284
1285 SRECT parseBox(char*str)
1286 {
1287     SRECT r;
1288     float xmin, xmax, ymin, ymax;
1289     char*x = strchr(str, 'x');
1290     char*d1=0,*d2=0;
1291     if(!strcmp(str, "autocrop")) {
1292         r.xmin = r.ymin = r.xmax = r.ymax = 0;
1293         return r;
1294     }
1295     if(!x) goto error;
1296     d1 = strchr(x+1, ':');
1297     if(d1)
1298         d2 = strchr(d1+1, ':');
1299     if(!d1) {
1300         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1301             goto error;
1302         xmin = ymin = 0;
1303     }
1304     else if(d1 && !d2) {
1305         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1306             goto error;
1307         xmax += xmin;
1308         ymin = 0;
1309     }
1310     else {
1311         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1312             goto error;
1313         xmax += xmin;
1314         ymax += ymin;
1315     }
1316     r.xmin = (SCOORD)(xmin*20);
1317     r.ymin = (SCOORD)(ymin*20);
1318     r.xmax = (SCOORD)(xmax*20);
1319     r.ymax = (SCOORD)(ymax*20);
1320     return r;
1321 error:
1322     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1323     return r;
1324 }
1325 float parseFloat(char*str)
1326 {
1327     return atof(str);
1328 }
1329 int parseInt(char*str)
1330 {
1331     int t;
1332     int l=strlen(str);
1333     int s=0;
1334     if(str[0]=='+' || str[0]=='-')
1335         s++;
1336
1337     for(t=s;t<l;t++)
1338         if(str[t]<'0' || str[t]>'9')
1339             syntaxerror("Not an Integer: \"%s\"", str);
1340     return atoi(str);
1341 }
1342 int parseTwip(char*str)
1343 {
1344     char*dot;
1345     int sign=1;
1346     if(str[0]=='+' || str[0]=='-') {
1347         if(str[0]=='-')
1348             sign = -1;
1349         str++;
1350     }
1351     dot = strchr(str, '.');
1352     if(!dot) {
1353         int l=strlen(str);
1354         int t;
1355         return sign*parseInt(str)*20;
1356     } else {
1357         int l=strlen(++dot);
1358         char*s;
1359         for(s=str;s<dot-1;s++)
1360             if(*s<'0' || *s>'9')
1361                 syntaxerror("Not a coordinate: \"%s\"", str);
1362         for(s=dot;*s;s++) {
1363             if(*s<'0' || *s>'9')
1364                 syntaxerror("Not a coordinate: \"%s\"", str);
1365         }
1366         if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1367             warning("precision loss: %s converted to twip", str);
1368             dot[2] = 0;
1369             l=2;
1370         }
1371         if(l==0)
1372             return sign*atoi(str)*20;
1373         if(l==1)
1374             return sign*atoi(str)*20+atoi(dot)*2;
1375         if(l==2)
1376             return sign*atoi(str)*20+atoi(dot)/5;
1377     }
1378     return 0;
1379 }
1380
1381 int isPoint(char*str)
1382 {
1383     if(strchr(str, '('))
1384         return 1;
1385     else
1386         return 0;
1387 }
1388
1389 SPOINT parsePoint(char*str)
1390 {
1391     SPOINT p;
1392     char tmp[80];
1393     int l = strlen(str);
1394     char*comma = strchr(str, ',');
1395     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1396         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1397     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1398     p.x = parseTwip(tmp);
1399     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1400     p.y = parseTwip(tmp);
1401     return p;
1402 }
1403
1404 int parseColor2(char*str, RGBA*color)
1405 {
1406     int l = strlen(str);
1407     int r,g,b,a;
1408     int t;
1409
1410     struct {unsigned char r,g,b;char*name;} colors[] =
1411     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1412     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1413     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1414     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1415     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1416     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1417     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1418     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1419     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1420     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1421     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1422     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1423
1424     a=255;r=g=b=0;
1425
1426     if(str[0]=='#' && (l==7 || l==9)) {
1427         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1428             return 0;
1429         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1430             return 0;
1431         color->r = r; color->g = g; color->b = b; color->a = a;
1432         return 1;
1433     }
1434     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1435         if(!strcmp(str, colors[t].name)) {
1436             r = colors[t].r;
1437             g = colors[t].g;
1438             b = colors[t].b;
1439             a = 255;
1440             color->r = r; color->g = g; color->b = b; color->a = a;
1441             return 1;
1442         }
1443     return 0;
1444
1445 }
1446 RGBA parseColor(char*str)
1447 {
1448     RGBA c;
1449     if(!parseColor2(str, &c))
1450         syntaxerror("Expression '%s' is not a color", str);
1451     return c;
1452 }
1453
1454 typedef struct _muladd {
1455     S16 mul;
1456     S16 add;
1457 } MULADD;
1458
1459 MULADD parseMulAdd(char*str)
1460 {
1461     float add, mul;
1462     char* str2 = (char*)malloc(strlen(str)+5);
1463     int i;
1464     MULADD m;
1465     strcpy(str2, str);
1466     strcat(str2, " 0");
1467     add = 0;
1468     mul = 1.0;
1469     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1470     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1471     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1472     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1473     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1474     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1475     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1476     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1477     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1478     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1479     else {
1480         syntaxerror("'%s' is not a valid color transform expression", str);
1481     }
1482     m.add = (int)(add*256);
1483     m.mul = (int)(mul*256);
1484     free(str2);
1485     return m;
1486 }
1487
1488 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1489 {
1490     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1491     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1492     MULADD r;
1493     if(a<-32768) a=-32768;
1494     if(a>32767) a=32767;
1495     if(m<-32768) m=-32768;
1496     if(m>32767) m=32767;
1497     r.add = a;
1498     r.mul = (int)m;
1499     return r;
1500 }
1501
1502 float parsePercent(char*str)
1503 {
1504     int l = strlen(str);
1505     if(!l)
1506         return 1.0;
1507     if(str[l-1]=='%') {
1508         return atoi(str)/100.0;
1509     }
1510     syntaxerror("Expression '%s' is not a percentage", str);
1511     return 0;
1512 }
1513 int isPercent(char*str)
1514 {
1515     return str[strlen(str)-1]=='%';
1516 }
1517 int parseNewSize(char*str, int size)
1518 {
1519     if(isPercent(str))
1520         return parsePercent(str)*size;
1521     else
1522         return (int)(atof(str)*20);
1523 }
1524
1525 int isColor(char*str)
1526 {
1527     RGBA c;
1528     return parseColor2(str, &c);
1529 }
1530
1531 static char* lu(map_t* args, char*name)
1532 {
1533     char* value = map_lookup(args, name);
1534     if(!value) {
1535         map_dump(args, stdout, "");
1536         syntaxerror("internal error 2: value %s should be set", name);
1537     }
1538     return value;
1539 }
1540
1541 static int c_flash(map_t*args) 
1542 {
1543     char* name = lu(args, "name");
1544     char* compressstr = lu(args, "compress");
1545     SRECT bbox = parseBox(lu(args, "bbox"));
1546     int version = parseInt(lu(args, "version"));
1547     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1548     int compress = 0;
1549     RGBA color = parseColor(lu(args, "background"));
1550     if(!strcmp(name, "!default!") || override_outputname)
1551         name = outputname;
1552     
1553     if(!strcmp(compressstr, "default"))
1554         compress = version==6;
1555     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1556         compress = 1;
1557     else if(!strcmp(compressstr, "no"))
1558         compress = 0;
1559     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1560
1561     s_swf(name, bbox, version, fps, compress, color);
1562     return 0;
1563 }
1564 int isRelative(char*str)
1565 {
1566     return !strncmp(str, "<plus>", 6) ||
1567            !strncmp(str, "<minus>", 7);
1568 }
1569 char* getOffset(char*str)
1570 {
1571     if(!strncmp(str, "<plus>", 6))
1572         return str+6;
1573     if(!strncmp(str, "<minus>", 7))
1574         return str+7;
1575     syntaxerror("internal error (347)");
1576     return 0;
1577 }
1578 int getSign(char*str)
1579 {
1580     if(!strncmp(str, "<plus>", 6))
1581         return 1;
1582     if(!strncmp(str, "<minus>", 7))
1583         return -1;
1584     syntaxerror("internal error (348)");
1585     return 0;
1586 }
1587 static dictionary_t points;
1588 static mem_t mpoints;
1589 int points_initialized = 0;
1590
1591 SPOINT getPoint(SRECT r, char*name)
1592 {
1593     int l=0;
1594     if(!strcmp(name, "center")) {
1595         SPOINT p;
1596         p.x = (r.xmin + r.xmax)/2;
1597         p.y = (r.ymin + r.ymax)/2;
1598         return p;
1599     }
1600
1601     if(points_initialized)
1602         l = (int)dictionary_lookup(&points, name);
1603     if(l==0) {
1604         syntaxerror("Invalid point: \"%s\".", name);
1605     }
1606     l--;
1607     return *(SPOINT*)&mpoints.buffer[l];
1608 }
1609 static int c_gradient(map_t*args) 
1610 {
1611     char*name = lu(args, "name");
1612     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
1613
1614     readToken();
1615     if(type != RAWDATA)
1616         syntaxerror("colon (:) expected");
1617
1618     s_gradient(name, text, radial);
1619     return 0;
1620 }
1621 static int c_point(map_t*args) 
1622 {
1623     char*name = lu(args, "name");
1624     int pos;
1625     string_t s1;
1626     SPOINT p;
1627     if(!points_initialized) {
1628         dictionary_init(&points);
1629         mem_init(&mpoints);
1630         points_initialized = 1;
1631     }
1632     p.x = parseTwip(lu(args, "x"));
1633     p.y = parseTwip(lu(args, "y"));
1634     pos = mem_put(&mpoints, &p, sizeof(p));
1635     string_set(&s1, name);
1636     pos++;
1637     dictionary_put(&points, s1, (void*)pos);
1638     return 0;
1639 }
1640 static int c_play(map_t*args) 
1641 {
1642     char*name = lu(args, "sound");
1643     char*loop = lu(args, "loop");
1644     char*nomultiple = lu(args, "nomultiple");
1645     int nm = 0;
1646     if(!strcmp(nomultiple, "nomultiple"))
1647         nm = 1;
1648     else
1649         nm = parseInt(nomultiple);
1650
1651     s_playsound(name, parseInt(loop), nm, 0);
1652     return 0;
1653 }
1654
1655 static int c_stop(map_t*args) 
1656 {
1657     char*name = lu(args, "sound");
1658     s_playsound(name, 0,0,1);
1659     return 0;
1660 }
1661
1662 static int c_placement(map_t*args, int type)
1663 {
1664     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1665     char*character = 0;
1666
1667     char* luminancestr = lu(args, "luminance");
1668     char* scalestr = lu(args, "scale");
1669     char* scalexstr = lu(args, "scalex");
1670     char* scaleystr = lu(args, "scaley");
1671     char* rotatestr = lu(args, "rotate");
1672     char* shearstr = lu(args, "shear");
1673     char* xstr="", *pivotstr="";
1674     char* ystr="", *anglestr="";
1675     char*above = lu(args, "above"); /*FIXME*/
1676     char*below = lu(args, "below");
1677     char* rstr = lu(args, "red");
1678     char* gstr = lu(args, "green");
1679     char* bstr = lu(args, "blue");
1680     char* astr = lu(args, "alpha");
1681     char* pinstr = lu(args, "pin");
1682     MULADD r,g,b,a;
1683     float oldwidth;
1684     float oldheight;
1685     SRECT oldbbox;
1686     MULADD luminance;
1687     parameters_t p;
1688
1689     if(type==5) {
1690         pivotstr = lu(args, "pivot");
1691         anglestr = lu(args, "angle");
1692     } else {
1693         xstr = lu(args, "x");
1694         ystr = lu(args, "y");
1695     }
1696     if(luminancestr[0])
1697         luminance = parseMulAdd(luminancestr);
1698     else {
1699         luminance.add = 0;
1700         luminance.mul = 256;
1701     }
1702
1703     if(scalestr[0]) {
1704         if(scalexstr[0]||scaleystr[0])
1705             syntaxerror("scalex/scaley and scale cannot both be set");
1706         scalexstr = scaleystr = scalestr;
1707     }
1708     
1709     if(type == 0 || type == 4)  {
1710         // put or startclip
1711         character = lu(args, "character");
1712         parameters_clear(&p);
1713     } else {
1714         p = s_getParameters(instance);
1715     }
1716
1717     /* x,y position */
1718     if(xstr[0]) {
1719         if(isRelative(xstr)) {
1720             if(type == 0 || type == 4)
1721                 syntaxerror("relative x values not allowed for initial put or startclip");
1722             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1723         } else {
1724             p.x = parseTwip(xstr);
1725         }
1726     }
1727     if(ystr[0]) {
1728         if(isRelative(ystr)) {
1729             if(type == 0 || type == 4)
1730                 syntaxerror("relative y values not allowed for initial put or startclip");
1731             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1732         } else {
1733             p.y = parseTwip(ystr);
1734         }
1735     }
1736
1737     /* scale, scalex, scaley */
1738     if(character) {
1739         oldbbox = s_getCharBBox(character);
1740     } else {
1741         oldbbox = s_getInstanceBBox(instance);
1742     }
1743     oldwidth = oldbbox.xmax - oldbbox.xmin;
1744     oldheight = oldbbox.ymax - oldbbox.ymin;
1745     if(scalexstr[0]) {
1746         if(oldwidth==0) p.scalex = 1.0;
1747         else {      
1748             if(scalexstr[0])
1749                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1750         }
1751     }
1752     if(scaleystr[0]) {
1753         if(oldheight==0) p.scaley = 1.0;
1754         else {
1755             if(scaleystr[0])
1756                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1757         }
1758     }
1759    
1760     /* rotation */
1761     if(rotatestr[0]) {
1762         if(isRelative(rotatestr)) {
1763             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1764         } else {
1765             p.rotate = parseFloat(rotatestr);
1766         }
1767     }
1768
1769     /* shearing */
1770     if(shearstr[0]) {
1771         if(isRelative(shearstr)) {
1772             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1773         } else {
1774             p.shear = parseFloat(shearstr);
1775         }
1776     }
1777
1778     if(pivotstr[0]) {
1779         if(isPoint(pivotstr)) 
1780             p.pivot = parsePoint(pivotstr);
1781         else 
1782             p.pivot = getPoint(oldbbox, pivotstr);
1783     }
1784     if(pinstr[0]) {
1785         if(isPoint(pinstr))
1786             p.pin = parsePoint(pinstr);
1787         else
1788             p.pin = getPoint(oldbbox, pinstr);
1789     }
1790         
1791     /* color transform */
1792
1793     if(rstr[0] || luminancestr[0]) {
1794         MULADD r;
1795         if(rstr[0])
1796             r = parseMulAdd(rstr);
1797         else {
1798             r.add = p.cxform.r0;
1799             r.mul = p.cxform.r1;
1800         }
1801         r = mergeMulAdd(r, luminance);
1802         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1803     }
1804     if(gstr[0] || luminancestr[0]) {
1805         MULADD g;
1806         if(gstr[0])
1807             g = parseMulAdd(gstr);
1808         else {
1809             g.add = p.cxform.g0;
1810             g.mul = p.cxform.g1;
1811         }
1812         g = mergeMulAdd(g, luminance);
1813         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1814     }
1815     if(bstr[0] || luminancestr[0]) {
1816         MULADD b;
1817         if(bstr[0])
1818             b = parseMulAdd(bstr);
1819         else {
1820             b.add = p.cxform.b0;
1821             b.mul = p.cxform.b1;
1822         }
1823         b = mergeMulAdd(b, luminance);
1824         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1825     }
1826     if(astr[0]) {
1827         MULADD a = parseMulAdd(astr);
1828         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1829     }
1830
1831     if(type == 0)
1832         s_put(instance, character, p);
1833     if(type == 1)
1834         s_change(instance, p);
1835     if(type == 2)
1836         s_qchange(instance, p);
1837     if(type == 3)
1838         s_jump(instance, p);
1839     if(type == 4)
1840         s_startclip(instance, character, p);
1841     return 0;
1842 }
1843 static int c_put(map_t*args) 
1844 {
1845     c_placement(args, 0);
1846     return 0;
1847 }
1848 static int c_change(map_t*args) 
1849 {
1850     c_placement(args, 1);
1851     return 0;
1852 }
1853 static int c_qchange(map_t*args) 
1854 {
1855     c_placement(args, 2);
1856     return 0;
1857 }
1858 static int c_arcchange(map_t*args) 
1859 {
1860     c_placement(args, 2);
1861     return 0;
1862 }
1863 static int c_jump(map_t*args) 
1864 {
1865     c_placement(args, 3);
1866     return 0;
1867 }
1868 static int c_startclip(map_t*args) 
1869 {
1870     c_placement(args, 4);
1871     return 0;
1872 }
1873 static int c_del(map_t*args) 
1874 {
1875     char*instance = lu(args, "name");
1876     s_delinstance(instance);
1877     return 0;
1878 }
1879 static int c_end(map_t*args) 
1880 {
1881     s_end();
1882     return 0;
1883 }
1884 static int c_sprite(map_t*args) 
1885 {
1886     char* name = lu(args, "name");
1887     s_sprite(name);
1888     return 0;
1889 }
1890 static int c_frame(map_t*args) 
1891 {
1892     char*framestr = lu(args, "n");
1893     char*cutstr = lu(args, "cut");
1894     int frame;
1895     int cut = 0;
1896     if(strcmp(cutstr, "no"))
1897         cut = 1;
1898     if(isRelative(framestr)) {
1899         frame = s_getframe();
1900         if(getSign(framestr)<0)
1901             syntaxerror("relative frame expressions must be positive");
1902         frame += parseInt(getOffset(framestr));
1903     }
1904     else {
1905         frame = parseInt(framestr);
1906         if(s_getframe() >= frame
1907                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1908             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1909     }
1910     s_frame(frame, cut);
1911     return 0;
1912 }
1913 static int c_primitive(map_t*args) 
1914 {
1915     char*name = lu(args, "name");
1916     char*command = lu(args, "commandname");
1917     int width=0, height=0, r=0;
1918     int linewidth = parseTwip(lu(args, "line"));
1919     char*colorstr = lu(args, "color");
1920     RGBA color = parseColor(colorstr);
1921     char*fillstr = lu(args, "fill");
1922     int dofill = 1;
1923     int type=0;
1924     char* font;
1925     char* text;
1926     char* outline=0;
1927     RGBA fill;
1928     if(!strcmp(command, "circle"))
1929         type = 1;
1930     else if(!strcmp(command, "filled"))
1931         type = 2;
1932    
1933     if(type==0) {
1934         width = parseTwip(lu(args, "width"));
1935         height = parseTwip(lu(args, "height"));
1936     } else if (type==1) {
1937         r = parseTwip(lu(args, "r"));
1938     } else if (type==2) {
1939         outline = lu(args, "outline");
1940     }
1941
1942     if(!strcmp(fillstr, "fill"))
1943         fillstr = colorstr;
1944     if(!strcmp(fillstr, "none"))
1945         fillstr = 0;
1946     if(width<0 || height<0 || linewidth<0 || r<0)
1947         syntaxerror("values width, height, line, r must be positive");
1948     
1949     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
1950     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
1951     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
1952     return 0;
1953 }
1954
1955 static int c_textshape(map_t*args) 
1956 {
1957     char*name = lu(args, "name");
1958     char*text = lu(args, "text");
1959     char*font = lu(args, "font");
1960
1961     s_textshape(name, font, text);
1962     return 0;
1963 }
1964
1965
1966
1967 static int c_swf(map_t*args) 
1968 {
1969     char*name = lu(args, "name");
1970     char*filename = lu(args, "filename");
1971     char*command = lu(args, "commandname");
1972     if(!strcmp(command, "shape"))
1973         warning("Please use .swf instead of .shape");
1974     s_includeswf(name, filename);
1975     return 0;
1976 }
1977
1978 static int c_font(map_t*args) 
1979 {
1980     char*name = lu(args, "name");
1981     char*filename = lu(args, "filename");
1982     s_font(name, filename);
1983     return 0;
1984 }
1985
1986 static int c_sound(map_t*args) 
1987 {
1988     char*name = lu(args, "name");
1989     char*filename = lu(args, "filename");
1990     s_sound(name, filename);
1991     return 0;
1992 }
1993
1994 static int c_text(map_t*args) 
1995 {
1996     char*name = lu(args, "name");
1997     char*text = lu(args, "text");
1998     char*font = lu(args, "font");
1999     float size = parsePercent(lu(args, "size"));
2000     RGBA color = parseColor(lu(args, "color"));
2001     s_text(name, font, text, (int)(size*100), color);
2002     return 0;
2003 }
2004
2005 static int c_soundtrack(map_t*args) 
2006 {
2007     return 0;
2008 }
2009
2010 static int c_image(map_t*args) 
2011 {
2012     char*command = lu(args, "commandname");
2013     char*name = lu(args, "name");
2014     char*filename = lu(args, "filename");
2015     if(!strcmp(command,"jpeg")) {
2016         int quality = (int)(parsePercent(lu(args, "quality"))*100);
2017         s_image(name, "jpeg", filename, quality);
2018     } else {
2019         s_image(name, "png", filename, 0);
2020     }
2021     return 0;
2022 }
2023
2024 static int c_outline(map_t*args) 
2025 {
2026     char*name = lu(args, "name");
2027     char*format = lu(args, "format");
2028
2029     readToken();
2030     if(type != RAWDATA)
2031         syntaxerror("colon (:) expected");
2032
2033     s_outline(name, format, text);
2034     return 0;
2035 }
2036
2037 int fakechar(map_t*args)
2038 {
2039     char*name = lu(args, "name");
2040     s_box(name, 0, 0, black, 20, 0);
2041     return 0;
2042 }
2043
2044 static int c_egon(map_t*args) {return fakechar(args);}
2045 static int c_button(map_t*args) {
2046     char*action = "";
2047     readToken();
2048     if(type == RAWDATA)
2049         action = text;
2050     else
2051         pushBack();
2052
2053     return fakechar(args);
2054 }
2055 static int c_edittext(map_t*args) {return fakechar(args);}
2056
2057 static int c_morphshape(map_t*args) {return fakechar(args);}
2058 static int c_movie(map_t*args) {return fakechar(args);}
2059
2060 static int c_buttonsounds(map_t*args) {return 0;}
2061 static int c_buttonput(map_t*args) {return 0;}
2062 static int c_texture(map_t*args) {return 0;}
2063
2064 static int c_action(map_t*args) 
2065 {
2066     readToken();
2067     if(type != RAWDATA)
2068         syntaxerror("colon (:) expected");
2069
2070     s_action(text);
2071    
2072     return 0;
2073 }
2074
2075 static struct {
2076     char*command;
2077     command_func_t* func;
2078     char*arguments;
2079 } arguments[] =
2080 {{"flash", c_flash, "bbox=autocrop background=black version=5 fps=50 name=!default! @compress=default"},
2081  {"frame", c_frame, "n=<plus>1 @cut=no"},
2082  // "import" type stuff
2083  {"swf", c_swf, "name filename"},
2084  {"shape", c_swf, "name filename"},
2085  {"jpeg", c_image, "name filename quality=80%"},
2086  {"png", c_image, "name filename"},
2087  {"movie", c_movie, "name filename"},
2088  {"sound", c_sound, "name filename"},
2089  {"font", c_font, "name filename"},
2090  {"soundtrack", c_soundtrack, "filename"},
2091
2092     // generators of primitives
2093
2094  {"point", c_point, "name x=0 y=0"},
2095  {"gradient", c_gradient, "name @radial=0"},
2096  {"outline", c_outline, "name format=simple"},
2097  {"textshape", c_textshape, "name text font"},
2098
2099     // character generators
2100  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2101  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2102  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2103
2104  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2105  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
2106  {"text", c_text, "name text font size=100% color=white"},
2107  {"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"},
2108  {"morphshape", c_morphshape, "name start end"},
2109  
2110  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
2111
2112     // control tags
2113  {"play", c_play, "sound loop=0 @nomultiple=0"},
2114  {"stop", c_stop, "sound"},
2115
2116     // object placement tags
2117  {"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="},
2118  {"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="},
2119  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2120  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2121  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2122  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
2123  {"del", c_del, "name"},
2124     // virtual object placement
2125  {"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="},
2126  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
2127
2128     // commands which start a block
2129 //startclip (see above)
2130  {"sprite", c_sprite, "name"},
2131  {"action", c_action, ""},
2132
2133  {"end", c_end, ""}
2134 };
2135
2136
2137 static map_t parseArguments(char*command, char*pattern)
2138 {
2139     char*x;
2140     char*d,*e;
2141    
2142     string_t name[64];
2143     string_t value[64];
2144     int set[64];
2145     int isboolean[64];
2146     int pos;
2147     int len;
2148     int t;
2149     string_t t1,t2;
2150     map_t result;
2151     map_init(&result);
2152
2153     string_set(&t1, "commandname");
2154     string_set(&t2, command);
2155     map_put(&result, t1, t2);
2156
2157     if(!pattern || !*pattern)
2158         return result;
2159
2160     x = pattern;
2161
2162     pos = 0;
2163
2164     if(!strncmp("<i> ", x, 3)) {
2165         readToken();
2166         if(type == COMMAND || type == RAWDATA) {
2167             pushBack();
2168             syntaxerror("character name expected");
2169         }
2170         name[pos].str = "instance";
2171         name[pos].len = 8;
2172         value[pos].str = text;
2173         value[pos].len = strlen(text);
2174         set[pos] = 1;
2175         pos++;
2176
2177         if(type == ASSIGNMENT)
2178             readToken();
2179
2180         name[pos].str = "character";
2181         name[pos].len = 9;
2182         value[pos].str = text;
2183         value[pos].len = strlen(text);
2184         set[pos] = 1;
2185         pos++;
2186
2187         x+=4;
2188     }
2189
2190     while(*x) {
2191         isboolean[pos] = (x[0] =='@');
2192         if(isboolean[pos])
2193             x++;
2194
2195         d = strchr(x, ' ');
2196         e = strchr(x, '=');
2197         if(!d)
2198             d=&x[strlen(x)];
2199         set[pos] = 0;
2200
2201         if(!e || d<e) { 
2202             // no default
2203             name[pos].str = x;
2204             name[pos].len = d-x;
2205             value[pos].str = 0;
2206             value[pos].len = 0;
2207         } else {
2208             name[pos].str = x;
2209             name[pos].len = e-x;
2210             value[pos].str = e+1;
2211             value[pos].len = d-e-1;
2212         }
2213         pos++;
2214         if(!*d) break;
2215         x=d+1;
2216     }
2217     len = pos;
2218
2219 /*    for(t=0;t<len;t++) {
2220         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
2221                 isboolean[t]?"(boolean)":"");
2222     }*/
2223
2224     while(1) {
2225         readToken();
2226         if(type == RAWDATA || type == COMMAND) {
2227             pushBack();
2228             break;
2229         }
2230
2231         // first, search for boolean arguments
2232         for(pos=0;pos<len;pos++)
2233         {
2234             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
2235                 set[pos] = 1;
2236                 if(type == ASSIGNMENT)
2237                     readToken();
2238                 value[pos].str = text;
2239                 value[pos].len = strlen(text);
2240                 /*printf("setting boolean parameter %s (to %s)\n",
2241                         strdup_n(name[pos], namelen[pos]),
2242                         strdup_n(value[pos], valuelen[pos]));*/
2243                 break;
2244             }
2245         }
2246
2247         // second, search for normal arguments
2248         if(pos==len)
2249         for(pos=0;pos<len;pos++)
2250         {
2251             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
2252                (type != ASSIGNMENT && !set[pos])) {
2253                 if(set[pos]) {
2254                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
2255                 }
2256                 if(type == ASSIGNMENT)
2257                     readToken();
2258                 set[pos] = 1;
2259                 value[pos].str = text;
2260                 value[pos].len = strlen(text);
2261 #if 0//def DEBUG
2262                 printf("setting parameter %s (to %s)\n",
2263                         strdup_n(name[pos].str, name[pos].len),
2264                         strdup_n(value[pos].str, value[pos].len));
2265 #endif
2266                 break;
2267             }
2268         }
2269         if(pos==len) {
2270             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
2271         }
2272     }
2273 #if 0//def DEBUG
2274     for(t=0;t<len;t++) {
2275         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
2276     }
2277 #endif
2278     for(t=0;t<len;t++) {
2279         if(value[t].str && value[t].str[0] == '*') {
2280             //relative default- take value from some other parameter
2281             int s;
2282             for(s=0;s<len;s++) {
2283                 if(value[s].len == value[t].len-1 &&
2284                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
2285                     value[t].str = value[s].str;
2286             }
2287         }
2288         if(value[t].str == 0) {
2289             pushBack();
2290             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
2291         }
2292     }
2293
2294     /* ok, now construct the dictionary from the parameters */
2295
2296     for(t=0;t<len;t++) 
2297     {
2298         map_put(&result, name[t], value[t]);
2299     }
2300     return result;
2301 }
2302 static void parseArgumentsForCommand(char*command)
2303 {
2304     int t;
2305     map_t args;
2306     int nr = -1;
2307     msg("<verbose> parse Command: %s (line %d)", command, line);
2308     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
2309         if(!strcmp(arguments[t].command, command)) {
2310
2311             /* ugly hack- will be removed soon (once documentation and .sc generating
2312                utilities have been changed) */
2313             if(!strcmp(command, "swf") && !stackpos) {
2314                 warning("Please use .flash instead of .swf- this will be mandatory soon");      
2315                 command = "flash";
2316                 t = 0;
2317             }
2318
2319             args = parseArguments(command, arguments[t].arguments);
2320             nr = t;
2321             break;
2322         }
2323     }
2324     if(nr<0)
2325         syntaxerror("command %s not known", command);
2326
2327 #ifdef DEBUG
2328     printf(".%s\n", command);fflush(stdout);
2329     map_dump(&args, stdout, "\t");fflush(stdout);
2330 #endif
2331
2332     (*arguments[nr].func)(&args);
2333
2334     /*if(!strcmp(command, "button") ||
2335        !strcmp(command, "action")) {
2336         while(1) {
2337             readToken();
2338             if(type == COMMAND) {
2339                 if(!strcmp(text, "end"))
2340                     break;
2341                 else {
2342                     pushBack();
2343                     break;
2344                 }
2345             }
2346         }
2347     }*/
2348
2349     map_clear(&args);
2350     return;
2351 }
2352
2353 int main (int argc,char ** argv)
2354
2355     int t;
2356     processargs(argc, argv);
2357     initLog(0,-1,0,0,-1,verbose);
2358
2359     if(!filename) {
2360         args_callback_usage(argv[0]);
2361         exit(1);
2362     }
2363     file = generateTokens(filename);
2364     if(!file) {
2365         printf("parser returned error.\n");
2366         return 1;
2367     }
2368     pos=0;
2369
2370     t=0;
2371
2372     while(!noMoreTokens()) {
2373         readToken();
2374         if(type != COMMAND)
2375             syntaxerror("command expected");
2376         parseArgumentsForCommand(text);
2377     }
2378
2379     s_close();
2380     freeTokens(file);
2381
2382     return 0;
2383 }
2384