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