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