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