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