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