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