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