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