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