added stdout support (-o -)
[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     s_addcharacter(name, id, tag, r);
984     incrementid();
985 }
986
987 void s_quicktime(char*name, char*url)
988 {
989     SRECT r;
990     MATRIX _m,*m=0;
991
992     memset(&r, 0, sizeof(r));
993     
994     tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
995     swf_SetU16(tag, id);
996     swf_SetString(tag, url);
997     
998     s_addcharacter(name, id, tag, r);
999     incrementid();
1000 }
1001
1002 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)
1003 {
1004     SWFFONT*font = 0;
1005     EditTextLayout layout;
1006     SRECT r;
1007
1008     if(fontname && *fontname) {
1009         flags |= ET_USEOUTLINES;
1010         font = dictionary_lookup(&fonts, fontname);
1011         if(!font)
1012             syntaxerror("font \"%s\" not known!", fontname);
1013     }
1014     tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1015     swf_SetU16(tag, id);
1016     layout.align = align;
1017     layout.leftmargin = 0;
1018     layout.rightmargin = 0;
1019     layout.indent = 0;
1020     layout.leading = 0;
1021     r.xmin = 0;
1022     r.ymin = 0;
1023     r.xmax = width;
1024     r.ymax = height;
1025     
1026     swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1027
1028     s_addcharacter(name, id, tag, r);
1029     incrementid();
1030 }
1031
1032 /* type: either "jpeg" or "png"
1033  */
1034 void s_image(char*name, char*type, char*filename, int quality)
1035 {
1036     /* an image is actually two folded: 1st bitmap, 2nd character.
1037        Both of them can be used separately */
1038     
1039     /* step 1: the bitmap */
1040     SRECT r;
1041     int imageID = id;
1042     int width, height;
1043     if(!strcmp(type,"jpeg")) {
1044 #ifndef HAVE_JPEGLIB
1045         warning("no jpeg support compiled in");
1046         s_box(name, 0, 0, black, 20, 0);
1047         return;
1048 #else
1049         tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1050         swf_SetU16(tag, imageID);
1051
1052         if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1053             syntaxerror("Image \"%s\" not found, or contains errors", filename);
1054         }
1055
1056         swf_GetJPEGSize(filename, &width, &height);
1057
1058         r.xmin = 0;
1059         r.ymin = 0;
1060         r.xmax = width*20;
1061         r.ymax = height*20;
1062
1063         s_addimage(name, id, tag, r);
1064         incrementid();
1065 #endif
1066     } else if(!strcmp(type,"png")) {
1067         RGBA*data = 0;
1068         swf_SetU16(tag, imageID);
1069
1070         getPNG(filename, &width, &height, (unsigned char**)&data);
1071
1072         if(!data) {
1073             syntaxerror("Image \"%s\" not found, or contains errors", filename);
1074         }
1075
1076         /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1077         tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1078         swf_SetU16(tag, imageID);
1079         swf_SetLosslessImage(tag, data, width, height);
1080
1081         r.xmin = 0;
1082         r.ymin = 0;
1083         r.xmax = width*20;
1084         r.ymax = height*20;
1085         s_addimage(name, id, tag, r);
1086         incrementid();
1087     } else {
1088         warning("image type \"%s\" not supported yet!", type);
1089         s_box(name, 0, 0, black, 20, 0);
1090         return;
1091     }
1092
1093     /* step 2: the character */
1094     tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1095     swf_SetU16(tag, id);
1096     swf_ShapeSetBitmapRect(tag, imageID, width, height);
1097
1098     s_addcharacter(name, id, tag, r);
1099     incrementid();
1100 }
1101
1102 void s_getBitmapSize(char*name, int*width, int*height)
1103 {
1104     character_t* image = dictionary_lookup(&images, name);
1105     gradient_t* gradient = dictionary_lookup(&gradients,name);
1106     if(image) {
1107         *width = image->size.xmax;
1108         *height = image->size.ymax;
1109         return;
1110     }
1111     if(gradient) {
1112         /* internal SWF gradient size */
1113         if(gradient->radial) {
1114             *width = 16384;
1115             *height = 16384;
1116         } else {
1117             *width = 32768;
1118             *height = 32768;
1119         }
1120         return;
1121     }
1122     syntaxerror("No such bitmap/gradient: %s", name);
1123 }
1124
1125 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1126 {
1127     gradient_t* gradient = dictionary_lookup(&gradients, object);
1128     character_t* bitmap = dictionary_lookup(&images, object);
1129     texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1130     parameters_t p;
1131     FILLSTYLE*fs = &texture->fs;
1132
1133     memset(&p, 0, sizeof(parameters_t));
1134
1135     if(bitmap) {
1136         fs->type = FILL_TILED;
1137         fs->id_bitmap = bitmap->id;
1138     } else if(gradient) {
1139         fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1140         fs->gradient = gradient->gradient;
1141     }
1142     p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1143     makeMatrix(&fs->m, &p);
1144     if(gradient && !gradient->radial) {
1145         MATRIX m = fs->m;
1146         SPOINT p1,p2;
1147         m.tx = 0;
1148         m.ty = 0;
1149         p1.x = 16384;
1150         p1.y = 16384;
1151         p2 = swf_TurnPoint(p1, &m);
1152         fs->m.tx += p2.x;
1153         fs->m.ty += p2.y;
1154     }
1155
1156     if(dictionary_lookup(&textures, name))
1157         syntaxerror("texture %s defined twice", name);
1158     dictionary_put2(&textures, name, texture);
1159 }
1160
1161 void dumpSWF(SWF*swf)
1162 {
1163     TAG* tag = swf->firstTag;
1164     printf("vvvvvvvvvvvvvvvvvvvvv\n");
1165     while(tag) {
1166         printf("%8d %s\n", tag->len, swf_TagGetName(tag));
1167         tag = tag->next;
1168     }
1169     printf("^^^^^^^^^^^^^^^^^^^^^\n");
1170 }
1171     
1172 void s_font(char*name, char*filename)
1173 {
1174     SWFFONT* font;
1175     font = swf_LoadFont(filename);
1176    
1177     if(font == 0) {
1178         warning("Couldn't open font file \"%s\"", filename);
1179         font = (SWFFONT*)malloc(sizeof(SWFFONT));
1180         memset(font, 0, sizeof(SWFFONT));
1181         dictionary_put2(&fonts, name, font);
1182         return;
1183     }
1184
1185     if(0)
1186     {
1187         /* fix the layout. Only needed for old fonts */
1188         int t;
1189         for(t=0;t<font->numchars;t++) {
1190             font->glyph[t].advance = 0;
1191         }
1192         font->layout = 0;
1193         swf_FontCreateLayout(font);
1194     }
1195     /* just in case this thing is used in .edittext later on */
1196     swf_FontPrepareForEditText(font);
1197
1198     font->id = id;
1199     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1200     swf_FontSetDefine2(tag, font);
1201     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1202     swf_SetU16(tag, 1);
1203     swf_SetU16(tag, id);
1204     swf_SetString(tag, name);
1205     incrementid();
1206
1207     if(dictionary_lookup(&fonts, name))
1208         syntaxerror("font %s defined twice", name);
1209     dictionary_put2(&fonts, name, font);
1210 }
1211
1212
1213
1214 typedef struct _sound_t
1215 {
1216     U16 id;
1217     TAG*tag;
1218 } sound_t;
1219
1220 void s_sound(char*name, char*filename)
1221 {
1222     struct WAV wav, wav2;
1223     struct MP3 mp3;
1224     sound_t* sound;
1225     U16*samples = NULL;
1226     unsigned numsamples;
1227     unsigned blocksize = 1152;
1228     int is_mp3 = 0;
1229
1230     if(wav_read(&wav, filename)) {
1231         int t;
1232         wav_convert2mono(&wav, &wav2, 44100);
1233         samples = (U16*)wav2.data;
1234         numsamples = wav2.size/2;
1235         free(wav.data);
1236 #ifdef WORDS_BIGENDIAN
1237         /* swap bytes */
1238         for(t=0;t<numsamples;t++) {
1239             samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1240         }
1241 #endif
1242     } else if(mp3_read(&mp3, filename)) {
1243         fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1244         blocksize = 1;
1245         is_mp3 = 1;
1246     }
1247     else
1248     {
1249         warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1250         samples = 0;
1251         numsamples = 0;
1252     }
1253     
1254     if(numsamples%blocksize != 0)
1255     {
1256         // apply padding, so that block is a multiple of blocksize
1257         int numblocks = (numsamples+blocksize-1)/blocksize;
1258         int numsamples2;
1259         U16* samples2;
1260         numsamples2 = numblocks * blocksize;
1261         samples2 = malloc(sizeof(U16)*numsamples2);
1262         memcpy(samples2, samples, numsamples*sizeof(U16));
1263         memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1264         numsamples = numsamples2;
1265         samples = samples2;
1266     }
1267
1268     tag = swf_InsertTag(tag, ST_DEFINESOUND);
1269     swf_SetU16(tag, id); //id
1270     if(is_mp3)
1271     {
1272         swf_SetSoundDefineMP3(
1273                 tag, mp3.data, mp3.size,
1274                 mp3.SampRate,
1275                 mp3.Channels,
1276                 mp3.NumFrames);
1277         mp3_clear(&mp3);
1278     }
1279     else
1280     {
1281         swf_SetSoundDefine(tag, samples, numsamples);
1282     }
1283     
1284     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1285     swf_SetU16(tag, id);
1286     swf_SetString(tag, name);
1287     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1288     swf_SetU16(tag, 1);
1289     swf_SetU16(tag, id);
1290     swf_SetString(tag, name);
1291
1292     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1293     sound->tag = tag;
1294     sound->id = id;
1295
1296     if(dictionary_lookup(&sounds, name))
1297         syntaxerror("sound %s defined twice", name);
1298     dictionary_put2(&sounds, name, sound);
1299     
1300     incrementid();
1301
1302     if(samples)
1303         free(samples);
1304 }
1305
1306 static char* gradient_getToken(const char**p)
1307 {
1308     const char*start;
1309     char*result;
1310     while(**p && strchr(" \t\n\r", **p)) {
1311         (*p)++;
1312     } 
1313     start = *p;
1314     while(**p && !strchr(" \t\n\r", **p)) {
1315         (*p)++;
1316     }
1317     result = malloc((*p)-start+1);
1318     memcpy(result,start,(*p)-start+1);
1319     result[(*p)-start] = 0;
1320     return result;
1321 }
1322
1323 float parsePercent(char*str);
1324 RGBA parseColor(char*str);
1325
1326 GRADIENT parseGradient(const char*str)
1327 {
1328     GRADIENT gradient;
1329     int lastpos = -1;
1330     const char* p = str;
1331     memset(&gradient, 0, sizeof(GRADIENT));
1332     gradient.ratios = rfx_calloc(16*sizeof(U8));
1333     gradient.rgba = rfx_calloc(16*sizeof(RGBA));
1334     while(*p) {
1335         char*posstr,*colorstr;
1336         int pos;
1337         RGBA color;
1338         posstr = gradient_getToken(&p);
1339         if(!*posstr)
1340             break;
1341         pos = (int)(parsePercent(posstr)*255.0);
1342         if(pos == lastpos)
1343             pos++;
1344         if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
1345         colorstr = gradient_getToken(&p);
1346         color = parseColor(colorstr);
1347         if(gradient.num == 16) {
1348             warning("gradient record too big- max size is 16, rest ignored");
1349             break;
1350         }
1351         gradient.ratios[gradient.num] = pos;
1352         gradient.rgba[gradient.num] = color;
1353         gradient.num++;
1354         free(posstr);
1355         free(colorstr);
1356         lastpos = pos;
1357     }
1358     return gradient;
1359 }
1360
1361 void s_gradient(char*name, const char*text, int radial, int rotate)
1362 {
1363     gradient_t* gradient;
1364     gradient = malloc(sizeof(gradient_t));
1365     memset(gradient, 0, sizeof(gradient_t));
1366     gradient->gradient = parseGradient(text);
1367     gradient->radial = radial;
1368     gradient->rotate = rotate;
1369
1370     if(dictionary_lookup(&gradients, name))
1371         syntaxerror("gradient %s defined twice", name);
1372     dictionary_put2(&gradients, name, gradient);
1373 }
1374     
1375 void s_gradientglow(char*name, char*gradient, float blurx, float blury, 
1376                     float angle, float distance, float strength, char innershadow, 
1377                     char knockout, char composite, char ontop, int passes)
1378 {
1379     gradient_t* g = dictionary_lookup(&gradients, gradient);
1380
1381     composite = 1;
1382
1383     if(!g)
1384         syntaxerror("unknown gradient %s", gradient);
1385     FILTER_GRADIENTGLOW* filter = rfx_calloc(sizeof(FILTER_GRADIENTGLOW));
1386     filter->type = FILTERTYPE_GRADIENTGLOW;
1387     filter->gradient = &g->gradient;
1388     filter->blurx = blurx;
1389     filter->blury = blury;
1390     filter->strength = strength;
1391     filter->angle = angle;
1392     filter->distance = distance;
1393     filter->innershadow = innershadow;
1394     filter->knockout = knockout;
1395     filter->composite = composite;
1396     filter->ontop = ontop;
1397     filter->passes = passes;
1398
1399     if(dictionary_lookup(&filters, name))
1400         syntaxerror("filter %s defined twice", name);
1401     dictionary_put2(&filters, name, filter);
1402 }
1403
1404 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)
1405 {
1406     composite = 1;
1407     FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
1408     filter->type = FILTERTYPE_DROPSHADOW;
1409     filter->color= color;
1410     filter->blurx = blurx;
1411     filter->blury = blury;
1412     filter->strength = strength;
1413     filter->angle = angle;
1414     filter->distance = distance;
1415     filter->innershadow = innershadow;
1416     filter->knockout = knockout;
1417     filter->composite = composite;
1418     filter->passes = passes;
1419
1420     if(dictionary_lookup(&filters, name))
1421         syntaxerror("filter %s defined twice", name);
1422     dictionary_put2(&filters, name, filter);
1423 }
1424
1425 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)
1426 {
1427     composite = 1;
1428     FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
1429     filter->type = FILTERTYPE_BEVEL;
1430     filter->shadow = shadow;
1431     filter->highlight = highlight;
1432     filter->blurx = blurx;
1433     filter->blury = blury;
1434     filter->strength = strength;
1435     filter->angle = angle;
1436     filter->distance = distance;
1437     filter->innershadow = innershadow;
1438     filter->knockout = knockout;
1439     filter->composite = composite;
1440     filter->ontop = ontop;
1441     filter->passes = passes;
1442
1443     if(dictionary_lookup(&filters, name))
1444         syntaxerror("filter %s defined twice", name);
1445     dictionary_put2(&filters, name, filter);
1446 }
1447
1448 void s_blur(char*name, double blurx, double blury, int passes)
1449 {
1450     FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
1451     filter->type = FILTERTYPE_BLUR;
1452     filter->blurx = blurx;
1453     filter->blury = blury;
1454     filter->passes = passes;
1455
1456     if(dictionary_lookup(&filters, name))
1457         syntaxerror("filter %s defined twice", name);
1458     dictionary_put2(&filters, name, filter);
1459 }
1460
1461 void s_action(const char*text)
1462 {
1463     ActionTAG* a = 0;
1464     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1465     if(!a) {
1466         syntaxerror("Couldn't compile ActionScript");
1467     }
1468
1469     tag = swf_InsertTag(tag, ST_DOACTION);
1470
1471     swf_ActionSet(tag, a);
1472
1473     swf_ActionFree(a);
1474 }
1475
1476 void s_initaction(const char*character, const char*text)
1477 {
1478     ActionTAG* a = 0;
1479     character_t*c = 0;
1480     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1481     if(!a) {
1482         syntaxerror("Couldn't compile ActionScript");
1483     }
1484
1485     c = (character_t*)dictionary_lookup(&characters, character);
1486
1487     tag = swf_InsertTag(tag, ST_DOINITACTION);
1488     swf_SetU16(tag, c->id);
1489     swf_ActionSet(tag, a);
1490
1491     swf_ActionFree(a);
1492 }
1493
1494 int s_swf3action(char*name, char*action)
1495 {
1496     ActionTAG* a = 0;
1497     instance_t* object = 0;
1498     if(name) 
1499         object = (instance_t*)dictionary_lookup(&instances, name);
1500     if(!object && name && *name) {
1501         /* we have a name, but couldn't find it. Abort. */
1502         return 0;
1503     }
1504     a = action_SetTarget(0, name);
1505     if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1506     else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1507     else if(!strcmp(action, "stop")) a = action_Stop(a);
1508     else if(!strcmp(action, "play")) a = action_Play(a);
1509     a = action_SetTarget(a, "");
1510     a = action_End(a);
1511
1512     tag = swf_InsertTag(tag, ST_DOACTION);
1513     swf_ActionSet(tag, a);
1514     swf_ActionFree(a);
1515     return 1;
1516 }
1517
1518 void s_outline(char*name, char*format, char*source)
1519 {
1520     outline_t* outline;
1521
1522     drawer_t draw;
1523     SHAPE* shape;
1524     SHAPE2* shape2;
1525     SRECT bounds;
1526     
1527     //swf_Shape10DrawerInit(&draw, 0);
1528     swf_Shape11DrawerInit(&draw, 0);
1529
1530     draw_string(&draw, source);
1531     draw.finish(&draw);
1532     shape = swf_ShapeDrawerToShape(&draw);
1533     bounds = swf_ShapeDrawerGetBBox(&draw);
1534     draw.dealloc(&draw);
1535     
1536     outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1537     outline->shape = shape;
1538     outline->bbox = bounds;
1539     
1540     if(dictionary_lookup(&outlines, name))
1541         syntaxerror("outline %s defined twice", name);
1542     dictionary_put2(&outlines, name, outline);
1543 }
1544
1545 int s_playsound(char*name, int loops, int nomultiple, int stop)
1546 {
1547     sound_t* sound;
1548     SOUNDINFO info;
1549     if(!name)
1550         return 0;
1551     sound = dictionary_lookup(&sounds, name);
1552     if(!sound)
1553         return 0;
1554
1555     tag = swf_InsertTag(tag, ST_STARTSOUND);
1556     swf_SetU16(tag, sound->id); //id
1557     memset(&info, 0, sizeof(info));
1558     info.stop = stop;
1559     info.loops = loops;
1560     info.nomultiple = nomultiple;
1561     swf_SetSoundInfo(tag, &info);
1562     return 1;
1563 }
1564
1565 void s_includeswf(char*name, char*filename)
1566 {
1567     int f;
1568     SWF swf;
1569     TAG* ftag;
1570     SRECT r;
1571     TAG* s;
1572     int level = 0;
1573     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1574     f = open(filename,O_RDONLY|O_BINARY);
1575     if (f<0) { 
1576         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1577         s_box(name, 0, 0, black, 20, 0);
1578         return;
1579     }
1580     if (swf_ReadSWF(f,&swf)<0) { 
1581         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1582         s_box(name, 0, 0, black, 20, 0);
1583         return;
1584     }
1585     close(f);
1586
1587     /* FIXME: The following sets the bounding Box for the character. 
1588               It is wrong for two reasons:
1589               a) It may be too small (in case objects in the movie clip at the borders)
1590               b) it may be too big (because the poor movie never got autocropped)
1591     */
1592     r = swf.movieSize;
1593     
1594     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1595     swf_SetU16(tag, id);
1596     swf_SetU16(tag, swf.frameCount);
1597
1598     swf_Relocate(&swf, idmap);
1599
1600     ftag = swf.firstTag;
1601     level = 1;
1602     while(ftag) {
1603         int t;
1604         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1605             if(cutout[t] == ftag->id) {
1606                 ftag = ftag->next;
1607                 continue;
1608             }
1609         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1610             level++;
1611         if(ftag->id == ST_END)
1612             level--;
1613         if(!level)
1614             break;
1615
1616         if(ftag->id != ST_SETBACKGROUNDCOLOR) {
1617             /* We simply dump all tags right after the sprite
1618                header, relying on the fact that swf_OptimizeTagOrder() will
1619                sort things out for us later. 
1620                We also rely on the fact that the imported SWF is well-formed.
1621              */
1622             tag = swf_InsertTag(tag, ftag->id);
1623             swf_SetBlock(tag, ftag->data, ftag->len);
1624         }
1625
1626         ftag = ftag->next;
1627     }
1628     if(!ftag)
1629         syntaxerror("Included file %s contains errors", filename);
1630     tag = swf_InsertTag(tag, ST_END);
1631
1632     swf_FreeTags(&swf);
1633
1634     s_addcharacter(name, id, tag, r);
1635     incrementid();
1636 }
1637 SRECT s_getCharBBox(char*name)
1638 {
1639     character_t* c = dictionary_lookup(&characters, name);
1640     if(!c) syntaxerror("character '%s' unknown(2)", name);
1641     return c->size;
1642 }
1643 SRECT s_getInstanceBBox(char*name)
1644 {
1645     instance_t * i = dictionary_lookup(&instances, name);
1646     character_t * c;
1647     if(!i) syntaxerror("instance '%s' unknown(4)", name);
1648     c = i->character;
1649     if(!c) syntaxerror("internal error(5)");
1650     return c->size;
1651 }
1652 parameters_t s_getParameters(char*name)
1653 {
1654     instance_t * i = dictionary_lookup(&instances, name);
1655     if(!i) syntaxerror("instance '%s' unknown(10)", name);
1656     return i->parameters;
1657 }
1658 void s_startclip(char*instance, char*character, parameters_t p)
1659 {
1660     character_t* c = dictionary_lookup(&characters, character);
1661     instance_t* i;
1662     MATRIX m;
1663     if(!c) {
1664         syntaxerror("character %s not known", character);
1665     }
1666     i = s_addinstance(instance, c, currentdepth);
1667     i->parameters = p;
1668     m = s_instancepos(i->character->size, &p);
1669     
1670     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1671     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1672     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1673     i->lastTag = tag;
1674     i->lastFrame= currentframe;
1675
1676     stack[stackpos].tag = tag;
1677     stack[stackpos].type = 2;
1678     stackpos++;
1679
1680     currentdepth++;
1681 }
1682 void s_endClip()
1683 {
1684     SWFPLACEOBJECT p;
1685     stackpos--;
1686     swf_SetTagPos(stack[stackpos].tag, 0);
1687     swf_GetPlaceObject(stack[stackpos].tag, &p);
1688     p.clipdepth = currentdepth;
1689     p.name = 0;
1690     swf_ClearTag(stack[stackpos].tag);
1691     swf_SetPlaceObject(stack[stackpos].tag, &p);
1692     currentdepth++;
1693 }
1694
1695 void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
1696 {
1697     SWFPLACEOBJECT po;
1698     FILTERLIST flist;
1699     swf_GetPlaceObject(NULL, &po);
1700     po.id = id;
1701     po.depth = depth;
1702     po.matrix = m;
1703     po.cxform = p->cxform;
1704     po.name = name;
1705     po.move = move;
1706     if(move)
1707         po.id = 0;
1708     if(p->blendmode) {
1709         po.blendmode = p->blendmode;
1710     }
1711     if(p->filter) {
1712         flist.num = 1;
1713         flist.filter[0] = p->filter;
1714         po.filters = &flist;
1715     }
1716     swf_SetPlaceObject(tag, &po);
1717 }
1718
1719 void s_put(char*instance, char*character, parameters_t p)
1720 {
1721     character_t* c = dictionary_lookup(&characters, character);
1722     instance_t* i;
1723     MATRIX m;
1724     if(!c) {
1725         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1726     }
1727     
1728     i = s_addinstance(instance, c, currentdepth);
1729     i->parameters = p;
1730     m = s_instancepos(i->character->size, &p);
1731    
1732     if(p.blendmode || p.filter) {
1733         tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
1734     } else {
1735         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1736     }
1737     setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
1738     
1739     i->lastTag = tag;
1740     i->lastFrame = currentframe;
1741     currentdepth++;
1742     
1743 }
1744
1745 void s_jump(char*instance, parameters_t p)
1746 {
1747     instance_t* i = dictionary_lookup(&instances, instance);
1748     MATRIX m;
1749     if(!i) {
1750         syntaxerror("instance %s not known", instance);
1751     }
1752
1753     i->parameters = p;
1754     m = s_instancepos(i->character->size, &p);
1755
1756     if(p.blendmode || p.filter) {
1757         tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
1758     } else {
1759         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1760     }
1761     setPlacement(tag, 0, i->depth, m, 0, &p, 1);
1762
1763     i->lastTag = tag;
1764     i->lastFrame = currentframe;
1765 }
1766
1767 RGBA interpolateColor(RGBA c1, RGBA c2, float ratio)
1768 {
1769     RGBA c;
1770     c.r = c1.r * (1-ratio) + c2.r * ratio;
1771     c.g = c1.g * (1-ratio) + c2.g * ratio;
1772     c.b = c1.b * (1-ratio) + c2.b * ratio;
1773     c.a = c1.a * (1-ratio) + c2.a * ratio;
1774     return c;
1775 }
1776
1777 FILTER* interpolateFilter(FILTER*filter1,FILTER*filter2, float ratio)
1778 {
1779     if(!filter1 && !filter2)
1780         return 0;
1781     if(!filter1)
1782         return interpolateFilter(filter2,filter1,1-ratio);
1783
1784     if(filter2 && filter2->type != filter1->type)
1785         syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
1786    
1787     if(filter1->type == FILTERTYPE_BLUR) {
1788         FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
1789         FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
1790         if(f2 && f1->blurx == f2->blurx && f1->blury == f2->blury)
1791             return 0;
1792         FILTER_BLUR*f = (FILTER_BLUR*)swf_NewFilter(FILTERTYPE_BLUR);
1793         f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
1794         f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
1795         f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
1796         return (FILTER*)f;
1797     } else if (filter1->type == FILTERTYPE_DROPSHADOW) {
1798         FILTER_DROPSHADOW*f1 = (FILTER_DROPSHADOW*)filter1;
1799         FILTER_DROPSHADOW*f2 = (FILTER_DROPSHADOW*)filter2;
1800         if(f2 && !memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength && 
1801            f1->blurx == f2->blurx && f1->blury == f2->blury && 
1802            f1->angle == f2->angle && f1->distance == f2->distance)
1803             return 0;
1804         FILTER_DROPSHADOW*f = (FILTER_DROPSHADOW*)swf_NewFilter(FILTERTYPE_DROPSHADOW);
1805         memcpy(f, f1, sizeof(FILTER_DROPSHADOW));
1806         f->color = interpolateColor(f1->color, f2->color, ratio);
1807         f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
1808         f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
1809         f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
1810         f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
1811         f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
1812         f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
1813         return (FILTER*)f;
1814     } else if (filter1->type == FILTERTYPE_BEVEL) {
1815         FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
1816         FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
1817         if(f2 && !memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) && 
1818            !memcmp(&f1->highlight,&f2->highlight,sizeof(RGBA)) && 
1819            f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
1820             return 0;
1821         FILTER_BEVEL*f = (FILTER_BEVEL*)swf_NewFilter(FILTERTYPE_BEVEL);
1822         memcpy(f, f1, sizeof(FILTER_BEVEL));
1823         f->shadow = interpolateColor(f1->shadow, f2->shadow, ratio);
1824         f->highlight = interpolateColor(f1->highlight, f2->highlight, 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_GRADIENTGLOW) {
1833         FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
1834         // can't interpolate gradients
1835         memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
1836         return (FILTER*)f;
1837     }*/ else {
1838         syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
1839     }
1840     return 0;
1841 }
1842
1843 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1844 {
1845     parameters_t p;
1846     float ratio;
1847     if(num==0 || num==1)
1848         return *p1;
1849     ratio = (float)pos/(float)num;
1850     
1851     p.x = (p2->x-p1->x)*ratio + p1->x;
1852     p.y = (p2->y-p1->y)*ratio + p1->y;
1853     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1854     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1855     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1856     p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1857
1858     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1859     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1860     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1861     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1862
1863     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1864     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1865     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1866     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1867
1868     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1869     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1870     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1871     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1872
1873     p.filter = interpolateFilter(p1->filter, p2->filter, ratio);
1874     return p;
1875 }
1876
1877 void s_change(char*instance, parameters_t p2)
1878 {
1879     instance_t* i = dictionary_lookup(&instances, instance);
1880     MATRIX m;
1881     parameters_t p1;
1882     TAG*t;
1883     int frame, allframes;
1884     if(!i) {
1885         syntaxerror("instance %s not known", instance);
1886     }
1887     p1 = i->parameters;
1888     
1889     allframes = currentframe - i->lastFrame - 1;
1890     if(allframes < 0) {
1891         warning(".change ignored. can only .put/.change an object once per frame.");
1892         return;
1893     }
1894     
1895     m = s_instancepos(i->character->size, &p2);
1896     if(p2.blendmode || p2.filter) {
1897         tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
1898     } else {
1899         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1900     }
1901     setPlacement(tag, 0, i->depth, m, 0, &p2, 1);
1902     i->parameters = p2;
1903
1904     /* o.k., we got the start and end point set. Now iterate though all the
1905        tags in between, inserting object changes after each new frame */
1906     t = i->lastTag;
1907     i->lastTag = tag;
1908     if(!t) syntaxerror("internal error(6)");
1909     frame = 0;
1910     while(frame < allframes) {
1911         if(t->id == ST_SHOWFRAME) {
1912             parameters_t p;
1913             MATRIX m;
1914             TAG*lt;
1915             frame ++;
1916             p = s_interpolate(&p1, &p2, frame, allframes);
1917             m = s_instancepos(i->character->size, &p); //needed?
1918
1919             i->lastFrame = currentframe;
1920             if(p.blendmode || p.filter) {
1921                 lt = swf_InsertTag(t, ST_PLACEOBJECT3);
1922             } else {
1923                 lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1924             }
1925             setPlacement(lt, 0, i->depth, m, 0, &p, 1);
1926             t = lt;
1927             if(frame == allframes)
1928                 break;
1929         }
1930         t = t->next;
1931         if(!t) 
1932             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1933     }
1934 }
1935
1936 void s_delinstance(char*instance)
1937 {
1938     instance_t* i = dictionary_lookup(&instances, instance);
1939     if(!i) {
1940         syntaxerror("instance %s not known", instance);
1941     }
1942     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1943     swf_SetU16(tag, i->depth);
1944     dictionary_del(&instances, instance);
1945 }
1946
1947 void s_qchange(char*instance, parameters_t p)
1948 {
1949 }
1950
1951 void s_end()
1952 {
1953     if(!stackpos)
1954         syntaxerror(".end unexpected");
1955     if(stack[stackpos-1].type == 0)
1956         s_endSWF();
1957     else if(stack[stackpos-1].type == 1)
1958         s_endSprite();
1959     else if(stack[stackpos-1].type == 2)
1960         s_endClip();
1961     else if(stack[stackpos-1].type == 3)
1962         s_endButton();
1963     else syntaxerror("internal error 1");
1964 }
1965
1966 // ------------------------------------------------------------------------
1967
1968 typedef int command_func_t(map_t*args);
1969
1970 SRECT parseBox(char*str)
1971 {
1972     SRECT r = {0,0,0,0};
1973     float xmin, xmax, ymin, ymax;
1974     char*x = strchr(str, 'x');
1975     char*d1=0,*d2=0;
1976     if(!strcmp(str, "autocrop")) {
1977         r.xmin = r.ymin = r.xmax = r.ymax = 0;
1978         return r;
1979     }
1980     if(!x) goto error;
1981     d1 = strchr(x+1, ':');
1982     if(d1)
1983         d2 = strchr(d1+1, ':');
1984     if(!d1) {
1985         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1986             goto error;
1987         xmin = ymin = 0;
1988     }
1989     else if(d1 && !d2) {
1990         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1991             goto error;
1992         xmax += xmin;
1993         ymin = 0;
1994     }
1995     else {
1996         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1997             goto error;
1998         xmax += xmin;
1999         ymax += ymin;
2000     }
2001     r.xmin = (SCOORD)(xmin*20);
2002     r.ymin = (SCOORD)(ymin*20);
2003     r.xmax = (SCOORD)(xmax*20);
2004     r.ymax = (SCOORD)(ymax*20);
2005     return r;
2006 error:
2007     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
2008     return r;
2009 }
2010 float parseFloat(char*str)
2011 {
2012     return atof(str);
2013 }
2014 int parseInt(char*str)
2015 {
2016     int t;
2017     int l=strlen(str);
2018     int s=0;
2019     if(str[0]=='+' || str[0]=='-')
2020         s++;
2021
2022     for(t=s;t<l;t++)
2023         if(str[t]<'0' || str[t]>'9')
2024             syntaxerror("Not an Integer: \"%s\"", str);
2025     return atoi(str);
2026 }
2027 int parseTwip(char*str)
2028 {
2029     char*dot;
2030     int sign=1;
2031     if(str[0]=='+' || str[0]=='-') {
2032         if(str[0]=='-')
2033             sign = -1;
2034         str++;
2035     }
2036     dot = strchr(str, '.');
2037     if(!dot) {
2038         int l=strlen(str);
2039         int t;
2040         return sign*parseInt(str)*20;
2041     } else {
2042         char* old = strdup(str);
2043         int l=strlen(dot+1);
2044         char*s;
2045         *dot++ = 0;
2046         for(s=str;s<dot-1;s++)
2047             if(*s<'0' || *s>'9')
2048                 syntaxerror("Not a coordinate: \"%s\"", str);
2049         for(s=dot;*s;s++) {
2050             if(*s<'0' || *s>'9')
2051                 syntaxerror("Not a coordinate: \"%s\"", str);
2052         }
2053         if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
2054             dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
2055             dot[2] = 0;
2056             l=2;
2057             warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
2058         }
2059         free(old);
2060         if(l==0)
2061             return sign*atoi(str)*20;
2062         if(l==1)
2063             return sign*atoi(str)*20+atoi(dot)*2;
2064         if(l==2)
2065             return sign*atoi(str)*20+atoi(dot)/5;
2066     }
2067     return 0;
2068 }
2069
2070 int isPoint(char*str)
2071 {
2072     if(strchr(str, '('))
2073         return 1;
2074     else
2075         return 0;
2076 }
2077
2078 SPOINT parsePoint(char*str)
2079 {
2080     SPOINT p;
2081     char tmp[80];
2082     int l = strlen(str);
2083     char*comma = strchr(str, ',');
2084     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
2085         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
2086     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
2087     p.x = parseTwip(tmp);
2088     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
2089     p.y = parseTwip(tmp);
2090     return p;
2091 }
2092
2093 int parseColor2(char*str, RGBA*color)
2094 {
2095     int l = strlen(str);
2096     int r,g,b,a;
2097     int t;
2098
2099     struct {unsigned char r,g,b;char*name;} colors[] =
2100     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
2101     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
2102     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
2103     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
2104     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
2105     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
2106     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
2107     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
2108     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
2109     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
2110     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
2111     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
2112
2113     a=255;r=g=b=0;
2114
2115     if(str[0]=='#' && (l==7 || l==9)) {
2116         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
2117             return 0;
2118         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
2119             return 0;
2120         color->r = r; color->g = g; color->b = b; color->a = a;
2121         return 1;
2122     }
2123     int len=strlen(str);
2124     U8 alpha = 255;
2125     if(strchr(str, '/')) {
2126         len = strchr(str, '/')-str;
2127         sscanf(str+len+1,"%02x", &alpha);
2128     }
2129     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
2130         if(!strncmp(str, colors[t].name, len)) {
2131             r = colors[t].r;
2132             g = colors[t].g;
2133             b = colors[t].b;
2134             a = alpha;
2135             color->r = r; color->g = g; color->b = b; color->a = a;
2136             return 1;
2137         }
2138     return 0;
2139
2140 }
2141 RGBA parseColor(char*str)
2142 {
2143     RGBA c;
2144     if(!parseColor2(str, &c))
2145         syntaxerror("Expression '%s' is not a color", str);
2146     return c;
2147 }
2148
2149 typedef struct _muladd {
2150     S16 mul;
2151     S16 add;
2152 } MULADD;
2153
2154 MULADD parseMulAdd(char*str)
2155 {
2156     float add, mul;
2157     char* str2 = (char*)malloc(strlen(str)+5);
2158     int i;
2159     MULADD m;
2160     strcpy(str2, str);
2161     strcat(str2, " 0");
2162     add = 0;
2163     mul = 1.0;
2164     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
2165     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
2166     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
2167     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
2168     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
2169     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
2170     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
2171     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
2172     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
2173     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
2174     else {
2175         syntaxerror("'%s' is not a valid color transform expression", str);
2176     }
2177     m.add = (int)(add*256);
2178     m.mul = (int)(mul*256);
2179     free(str2);
2180     return m;
2181 }
2182
2183 MULADD mergeMulAdd(MULADD m1, MULADD m2)
2184 {
2185     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
2186     double m = ((double)m1.mul*(double)m2.mul)/256.0;
2187     MULADD r;
2188     if(a<-32768) a=-32768;
2189     if(a>32767) a=32767;
2190     if(m<-32768) m=-32768;
2191     if(m>32767) m=32767;
2192     r.add = a;
2193     r.mul = (int)m;
2194     return r;
2195 }
2196
2197 float parsePxOrPercent(char*fontname, char*str)
2198 {
2199     int l = strlen(str);
2200     if(strchr(str, '%'))
2201         return parsePercent(str);
2202     if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
2203         float p = atof(str);
2204         return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
2205     }
2206     syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
2207     return 0;
2208 }
2209
2210 float parsePercent(char*str)
2211 {
2212     int l = strlen(str);
2213     if(!l)
2214         return 1.0;
2215     if(str[l-1]=='%') {
2216         return atoi(str)/100.0;
2217     }
2218     syntaxerror("Expression '%s' is not a percentage", str);
2219     return 0;
2220 }
2221 int isPercent(char*str)
2222 {
2223     return str[strlen(str)-1]=='%';
2224 }
2225 int parseNewSize(char*str, int size)
2226 {
2227     if(isPercent(str))
2228         return parsePercent(str)*size;
2229     else
2230         return (int)(atof(str)*20);
2231 }
2232
2233 int isColor(char*str)
2234 {
2235     RGBA c;
2236     return parseColor2(str, &c);
2237 }
2238
2239 static char* lu(map_t* args, char*name)
2240 {
2241     char* value = map_lookup(args, name);
2242     if(!value) {
2243         map_dump(args, stdout, "");
2244         syntaxerror("internal error 2: value %s should be set", name);
2245     }
2246     return value;
2247 }
2248
2249 static int c_flash(map_t*args) 
2250 {
2251     char* filename = map_lookup(args, "filename");
2252     char* compressstr = lu(args, "compress");
2253     SRECT bbox = parseBox(lu(args, "bbox"));
2254     int version = parseInt(lu(args, "version"));
2255     int fps = (int)(parseFloat(lu(args, "fps"))*256);
2256     int compress = 0;
2257     RGBA color = parseColor(lu(args, "background"));
2258
2259     if(!filename || !*filename) {
2260         /* for compatibility */
2261         filename = map_lookup(args, "name");
2262         if(!filename || !*filename) {
2263             filename = 0;
2264         } else {
2265             //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2266             msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2267         }
2268     }
2269
2270     if(!filename || override_outputname)
2271         filename = outputname;
2272     
2273     if(!strcmp(compressstr, "default"))
2274         compress = version>=6;
2275     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2276         compress = 1;
2277     else if(!strcmp(compressstr, "no"))
2278         compress = 0;
2279     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2280
2281     s_swf(filename, bbox, version, fps, compress, color);
2282     return 0;
2283 }
2284 int isRelative(char*str)
2285 {
2286     return !strncmp(str, "<plus>", 6) ||
2287            !strncmp(str, "<minus>", 7);
2288 }
2289 char* getOffset(char*str)
2290 {
2291     if(!strncmp(str, "<plus>", 6))
2292         return str+6;
2293     if(!strncmp(str, "<minus>", 7))
2294         return str+7;
2295     syntaxerror("internal error (347)");
2296     return 0;
2297 }
2298 int getSign(char*str)
2299 {
2300     if(!strncmp(str, "<plus>", 6))
2301         return 1;
2302     if(!strncmp(str, "<minus>", 7))
2303         return -1;
2304     syntaxerror("internal error (348)");
2305     return 0;
2306 }
2307 static dictionary_t points;
2308 static mem_t mpoints;
2309 int points_initialized = 0;
2310
2311 SPOINT getPoint(SRECT r, char*name)
2312 {
2313     int l=0;
2314     if(!strcmp(name, "center")) {
2315         SPOINT p;
2316         p.x = (r.xmin + r.xmax)/2;
2317         p.y = (r.ymin + r.ymax)/2;
2318         return p;
2319     }
2320
2321     if(points_initialized)
2322         l = (int)dictionary_lookup(&points, name);
2323     if(l==0) {
2324         syntaxerror("Invalid point: \"%s\".", name);
2325     }
2326     l--;
2327     return *(SPOINT*)&mpoints.buffer[l];
2328 }
2329
2330 static int texture2(char*name, char*object, map_t*args, int errors) 
2331 {
2332     SPOINT pos,size;
2333     char*xstr = map_lookup(args, "x");
2334     char*ystr = map_lookup(args, "y");
2335     char*widthstr = map_lookup(args, "width");
2336     char*heightstr = map_lookup(args, "height");
2337     char*scalestr = map_lookup(args, "scale");
2338     char*scalexstr = map_lookup(args, "scalex");
2339     char*scaleystr = map_lookup(args, "scaley");
2340     char*rotatestr = map_lookup(args, "rotate");
2341     char* shearstr = map_lookup(args, "shear");
2342     char* radiusstr = map_lookup(args, "r");
2343     float x=0,y=0;
2344     float scalex = 1.0, scaley = 1.0;
2345     float rotate=0, shear=0;
2346     float r = 0;
2347     if(!*xstr && !*ystr) {
2348         if(errors)
2349             syntaxerror("x and y must be set");
2350         return 0;
2351     }
2352     if(*scalestr && (*scalexstr || *scaleystr)) {
2353         syntaxerror("scale and scalex/scaley can't both be set");
2354         return 0;
2355     }
2356     if((*widthstr || *heightstr) && *radiusstr) {
2357         syntaxerror("width/height and radius can't both be set");
2358     }
2359     if(*radiusstr) {
2360         widthstr = radiusstr;
2361         heightstr = radiusstr;
2362     }
2363     if(!*xstr) xstr="0";
2364     if(!*ystr) ystr="0";
2365     if(!*rotatestr) rotatestr="0";
2366     if(!*shearstr) shearstr="0";
2367
2368     if(*scalestr) {
2369         scalex = scaley = parsePercent(scalestr);
2370     } else if(*scalexstr || *scaleystr) {
2371         if(scalexstr) scalex = parsePercent(scalexstr);
2372         if(scaleystr) scaley = parsePercent(scaleystr);
2373     } else if(*widthstr || *heightstr) {
2374         int width=0;
2375         int height=0;
2376         s_getBitmapSize(object, &width, &height);
2377         if(*widthstr)
2378             scalex = (float)parseTwip(widthstr)/(float)width;
2379         if(*heightstr)
2380             scaley = (float)parseTwip(heightstr)/(float)height;
2381     }
2382     x = parseTwip(xstr);
2383     y = parseTwip(ystr);
2384     rotate = parseFloat(rotatestr);
2385     shear = parseFloat(shearstr);
2386
2387     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2388
2389     return 0;
2390 }
2391
2392 static int c_texture(map_t*args) 
2393 {
2394     char*name = lu(args, "instance");
2395     char*object = lu(args, "character");
2396     return texture2(name, object, args, 1);
2397 }
2398
2399 static int c_gradient(map_t*args) 
2400 {
2401     char*name = lu(args, "name");
2402     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2403     int rotate = parseInt(lu(args, "rotate"));
2404
2405     readToken();
2406     if(type != RAWDATA)
2407         syntaxerror("colon (:) expected");
2408
2409     s_gradient(name, text, radial, rotate);
2410
2411     /* check whether we also have placement information,
2412        which would make this a positioned gradient.
2413        If there is placement information, texture2() will
2414        add a texture, which has priority over the gradient.
2415      */
2416     texture2(name, name, args, 0);
2417     return 0;
2418 }
2419
2420 static int c_blur(map_t*args) 
2421 {
2422     char*name = lu(args, "name");
2423     char*blurstr = lu(args, "blur");
2424     char*blurxstr = lu(args, "blurx");
2425     char*blurystr = lu(args, "blury");
2426     float blurx=1.0, blury=1.0;
2427     if(blurstr[0]) {
2428         blurx = parseFloat(blurstr);
2429         blury = parseFloat(blurstr);
2430     } 
2431     if(blurxstr[0])
2432         blurx = parseFloat(blurxstr);
2433     if(blurystr[0])
2434         blury = parseFloat(blurystr);
2435     int passes = parseInt(lu(args, "passes"));
2436     s_blur(name, blurx, blury, passes);
2437     return 0;
2438 }
2439
2440 static int c_gradientglow(map_t*args) 
2441 {
2442     char*name = lu(args, "name");
2443     char*gradient = lu(args, "gradient");
2444     char*blurstr = lu(args, "blur");
2445     char*blurxstr = lu(args, "blurx");
2446     char*blurystr = lu(args, "blury");
2447     float blurx=1.0, blury=1.0;
2448     if(blurstr[0]) {
2449         blurx = parseFloat(blurstr);
2450         blury = parseFloat(blurstr);
2451     } 
2452     if(blurxstr[0])
2453         blurx = parseFloat(blurxstr);
2454     if(blurystr[0])
2455         blury = parseFloat(blurystr);
2456
2457     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2458     float distance = parseFloat(lu(args, "distance"));
2459     float strength = parseFloat(lu(args, "strength"));
2460     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2461     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2462     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2463     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2464     int passes = parseInt(lu(args, "passes"));
2465
2466     s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2467     return 0;
2468 }
2469
2470 static int c_dropshadow(map_t*args) 
2471 {
2472     char*name = lu(args, "name");
2473     RGBA color = parseColor(lu(args, "color"));
2474     char*blurstr = lu(args, "blur");
2475     char*blurxstr = lu(args, "blurx");
2476     char*blurystr = lu(args, "blury");
2477     float blurx=1.0, blury=1.0;
2478     if(blurstr[0]) {
2479         blurx = parseFloat(blurstr);
2480         blury = parseFloat(blurstr);
2481     } 
2482     if(blurxstr[0])
2483         blurx = parseFloat(blurxstr);
2484     if(blurystr[0])
2485         blury = parseFloat(blurystr);
2486
2487     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2488     float distance = parseFloat(lu(args, "distance"));
2489     float strength = parseFloat(lu(args, "strength"));
2490     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2491     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2492     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2493     int passes = parseInt(lu(args, "passes"));
2494
2495     s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
2496     return 0;
2497 }
2498
2499 static int c_bevel(map_t*args) 
2500 {
2501     char*name = lu(args, "name");
2502     RGBA shadow = parseColor(lu(args, "shadow"));
2503     RGBA highlight = parseColor(lu(args, "highlight"));
2504     char*blurstr = lu(args, "blur");
2505     char*blurxstr = lu(args, "blurx");
2506     char*blurystr = lu(args, "blury");
2507     float blurx=1.0, blury=1.0;
2508     if(blurstr[0]) {
2509         blurx = parseFloat(blurstr);
2510         blury = parseFloat(blurstr);
2511     } 
2512     if(blurxstr[0])
2513         blurx = parseFloat(blurxstr);
2514     if(blurystr[0])
2515         blury = parseFloat(blurystr);
2516
2517     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2518     float distance = parseFloat(lu(args, "distance"));
2519     float strength = parseFloat(lu(args, "strength"));
2520     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2521     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2522     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2523     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2524     int passes = parseInt(lu(args, "passes"));
2525
2526     s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2527     return 0;
2528 }
2529
2530 static int c_point(map_t*args) 
2531 {
2532     char*name = lu(args, "name");
2533     int pos;
2534     string_t s1;
2535     SPOINT p;
2536     if(!points_initialized) {
2537         dictionary_init(&points);
2538         mem_init(&mpoints);
2539         points_initialized = 1;
2540     }
2541     p.x = parseTwip(lu(args, "x"));
2542     p.y = parseTwip(lu(args, "y"));
2543     pos = mem_put(&mpoints, &p, sizeof(p));
2544     string_set(&s1, name);
2545     pos++;
2546     dictionary_put(&points, s1, (void*)pos);
2547     return 0;
2548 }
2549 static int c_play(map_t*args) 
2550 {
2551     char*name = lu(args, "name");
2552     char*loop = lu(args, "loop");
2553     char*nomultiple = lu(args, "nomultiple");
2554     int nm = 0;
2555     if(!strcmp(nomultiple, "nomultiple"))
2556         nm = 1;
2557     else
2558         nm = parseInt(nomultiple);
2559
2560     if(s_playsound(name, parseInt(loop), nm, 0)) {
2561         return 0;
2562     } else if(s_swf3action(name, "play")) {
2563         return 0;
2564     }
2565     return 0;
2566 }
2567
2568 static int c_stop(map_t*args) 
2569 {
2570     char*name = map_lookup(args, "name");
2571
2572     if(s_playsound(name, 0,0,1)) {
2573         return 0;
2574     } else if(s_swf3action(name, "stop")) {
2575         return 0;
2576     }
2577     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2578     return 0;
2579 }
2580
2581 static int c_nextframe(map_t*args) 
2582 {
2583     char*name = lu(args, "name");
2584
2585     if(s_swf3action(name, "nextframe")) {
2586         return 0;
2587     }
2588     syntaxerror("I don't know anything about movie \"%s\"", name);
2589     return 0;
2590 }
2591
2592 static int c_previousframe(map_t*args) 
2593 {
2594     char*name = lu(args, "name");
2595
2596     if(s_swf3action(name, "previousframe")) {
2597         return 0;
2598     }
2599     syntaxerror("I don't know anything about movie \"%s\"", name);
2600     return 0;
2601 }
2602
2603 static int c_placement(map_t*args, int type)
2604 {
2605     char*instance = lu(args, (type==0||type==4)?"instance":"name");
2606     char*character = 0;
2607
2608     char* luminancestr = lu(args, "luminance");
2609     char* scalestr = lu(args, "scale");
2610     char* scalexstr = lu(args, "scalex");
2611     char* scaleystr = lu(args, "scaley");
2612     char* rotatestr = lu(args, "rotate");
2613     char* shearstr = lu(args, "shear");
2614     char* xstr="", *pivotstr="";
2615     char* ystr="", *anglestr="";
2616     char*above = lu(args, "above"); /*FIXME*/
2617     char*below = lu(args, "below");
2618     char* rstr = lu(args, "red");
2619     char* gstr = lu(args, "green");
2620     char* bstr = lu(args, "blue");
2621     char* astr = lu(args, "alpha");
2622     char* pinstr = lu(args, "pin");
2623     char* as = map_lookup(args, "as");
2624     char* blendmode = lu(args, "blend");
2625     char*filterstr = lu(args, "filter");
2626     U8 blend;
2627     MULADD r,g,b,a;
2628     float oldwidth;
2629     float oldheight;
2630     SRECT oldbbox;
2631     MULADD luminance;
2632     parameters_t p;
2633
2634     if(type==9) { // (?) .rotate  or .arcchange
2635         pivotstr = lu(args, "pivot");
2636         anglestr = lu(args, "angle");
2637     } else {
2638         xstr = lu(args, "x");
2639         ystr = lu(args, "y");
2640     }
2641     if(luminancestr[0])
2642         luminance = parseMulAdd(luminancestr);
2643     else {
2644         luminance.add = 0;
2645         luminance.mul = 256;
2646     }
2647
2648     if(scalestr[0]) {
2649         if(scalexstr[0]||scaleystr[0])
2650             syntaxerror("scalex/scaley and scale cannot both be set");
2651         scalexstr = scaleystr = scalestr;
2652     }
2653     
2654     if(type == 0 || type == 4)  {
2655         // put or startclip
2656         character = lu(args, "character");
2657         parameters_clear(&p);
2658     } else if (type == 5) {
2659         character = lu(args, "name");
2660         parameters_clear(&p);
2661         // button's show
2662     } else {
2663         p = s_getParameters(instance);
2664     }
2665
2666     /* x,y position */
2667     if(xstr[0]) {
2668         if(isRelative(xstr)) {
2669             if(type == 0 || type == 4)
2670                 syntaxerror("relative x values not allowed for initial put or startclip");
2671             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2672         } else {
2673             p.x = parseTwip(xstr);
2674         }
2675     }
2676     if(ystr[0]) {
2677         if(isRelative(ystr)) {
2678             if(type == 0 || type == 4)
2679                 syntaxerror("relative y values not allowed for initial put or startclip");
2680             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2681         } else {
2682             p.y = parseTwip(ystr);
2683         }
2684     }
2685
2686     /* scale, scalex, scaley */
2687     if(character) {
2688         oldbbox = s_getCharBBox(character);
2689     } else {
2690         oldbbox = s_getInstanceBBox(instance);
2691     }
2692     oldwidth = oldbbox.xmax - oldbbox.xmin;
2693     oldheight = oldbbox.ymax - oldbbox.ymin;
2694     if(scalexstr[0]) {
2695         if(oldwidth==0) p.scalex = 1.0;
2696         else {      
2697             if(scalexstr[0])
2698                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2699         }
2700     }
2701     if(scaleystr[0]) {
2702         if(oldheight==0) p.scaley = 1.0;
2703         else {
2704             if(scaleystr[0])
2705                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2706         }
2707     }
2708    
2709     /* rotation */
2710     if(rotatestr[0]) {
2711         if(isRelative(rotatestr)) {
2712             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2713         } else {
2714             p.rotate = parseFloat(rotatestr);
2715         }
2716     }
2717
2718     /* shearing */
2719     if(shearstr[0]) {
2720         if(isRelative(shearstr)) {
2721             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2722         } else {
2723             p.shear = parseFloat(shearstr);
2724         }
2725     }
2726
2727     if(pivotstr[0]) {
2728         if(isPoint(pivotstr)) 
2729             p.pivot = parsePoint(pivotstr);
2730         else 
2731             p.pivot = getPoint(oldbbox, pivotstr);
2732     }
2733     if(pinstr[0]) {
2734         if(isPoint(pinstr))
2735             p.pin = parsePoint(pinstr);
2736         else
2737             p.pin = getPoint(oldbbox, pinstr);
2738     }
2739         
2740     /* color transform */
2741
2742     if(rstr[0] || luminancestr[0]) {
2743         MULADD r;
2744         if(rstr[0])
2745             r = parseMulAdd(rstr);
2746         else {
2747             r.add = p.cxform.r0;
2748             r.mul = p.cxform.r1;
2749         }
2750         r = mergeMulAdd(r, luminance);
2751         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2752     }
2753     if(gstr[0] || luminancestr[0]) {
2754         MULADD g;
2755         if(gstr[0])
2756             g = parseMulAdd(gstr);
2757         else {
2758             g.add = p.cxform.g0;
2759             g.mul = p.cxform.g1;
2760         }
2761         g = mergeMulAdd(g, luminance);
2762         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2763     }
2764     if(bstr[0] || luminancestr[0]) {
2765         MULADD b;
2766         if(bstr[0])
2767             b = parseMulAdd(bstr);
2768         else {
2769             b.add = p.cxform.b0;
2770             b.mul = p.cxform.b1;
2771         }
2772         b = mergeMulAdd(b, luminance);
2773         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2774     }
2775     if(astr[0]) {
2776         MULADD a = parseMulAdd(astr);
2777         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2778     }
2779
2780     if(blendmode[0]) {
2781         int t;
2782         blend = 255;
2783         for(t=0;blendModeNames[t];t++) {
2784             if(!strcmp(blendModeNames[t], blendmode)) {
2785                 blend = t;
2786                 break;
2787             }
2788         }
2789         if(blend == 255) {
2790             syntaxerror("unknown blend mode: '%s'", blendmode);
2791         }
2792         p.blendmode = blend;
2793     }
2794     
2795     if(filterstr[0]) {
2796         FILTER*f = dictionary_lookup(&filters, filterstr);
2797         if(!f) 
2798             syntaxerror("Unknown filter %s", filterstr);
2799         p.filter = f;
2800     }
2801
2802
2803     if(type == 0)
2804         s_put(instance, character, p);
2805     else if(type == 1)
2806         s_change(instance, p);
2807     else if(type == 2)
2808         s_qchange(instance, p);
2809     else if(type == 3)
2810         s_jump(instance, p);
2811     else if(type == 4)
2812         s_startclip(instance, character, p);
2813     else if(type == 5) {
2814         if(as && as[0]) {
2815             s_buttonput(character, as, p);
2816         } else {
2817             s_buttonput(character, "shape", p);
2818         }
2819     }
2820     return 0;
2821 }
2822 static int c_put(map_t*args) 
2823 {
2824     c_placement(args, 0);
2825     return 0;
2826 }
2827 static int c_change(map_t*args) 
2828 {
2829     c_placement(args, 1);
2830     return 0;
2831 }
2832 static int c_qchange(map_t*args) 
2833 {
2834     c_placement(args, 2);
2835     return 0;
2836 }
2837 static int c_arcchange(map_t*args) 
2838 {
2839     c_placement(args, 2);
2840     return 0;
2841 }
2842 static int c_jump(map_t*args) 
2843 {
2844     c_placement(args, 3);
2845     return 0;
2846 }
2847 static int c_startclip(map_t*args) 
2848 {
2849     c_placement(args, 4);
2850     return 0;
2851 }
2852 static int c_show(map_t*args) 
2853 {
2854     c_placement(args, 5);
2855     return 0;
2856 }
2857 static int c_del(map_t*args) 
2858 {
2859     char*instance = lu(args, "name");
2860     s_delinstance(instance);
2861     return 0;
2862 }
2863 static int c_end(map_t*args) 
2864 {
2865     s_end();
2866     return 0;
2867 }
2868 static int c_sprite(map_t*args) 
2869 {
2870     char* name = lu(args, "name");
2871     s_sprite(name);
2872     return 0;
2873 }
2874 static int c_frame(map_t*args) 
2875 {
2876     char*framestr = lu(args, "n");
2877     char*cutstr = lu(args, "cut");
2878     
2879     char*name = lu(args, "name");
2880     char*anchor = lu(args, "anchor");
2881     char buf[40];
2882
2883     if(!strcmp(anchor, "anchor") && !*name)
2884         name = framestr;
2885
2886     int frame;
2887     int cut = 0;
2888     if(strcmp(cutstr, "no"))
2889         cut = 1;
2890     if(isRelative(framestr)) {
2891         frame = s_getframe();
2892         if(getSign(framestr)<0)
2893             syntaxerror("relative frame expressions must be positive");
2894         frame += parseInt(getOffset(framestr));
2895     }
2896     else {
2897         frame = parseInt(framestr);
2898         if(s_getframe() >= frame
2899                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2900             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2901     }
2902     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2903     return 0;
2904 }
2905 static int c_primitive(map_t*args) 
2906 {
2907     char*name = lu(args, "name");
2908     char*command = lu(args, "commandname");
2909     int width=0, height=0, r=0;
2910     int linewidth = parseTwip(lu(args, "line"));
2911     char*colorstr = lu(args, "color");
2912     RGBA color = parseColor(colorstr);
2913     char*fillstr = lu(args, "fill");
2914     int dofill = 1;
2915     int type=0;
2916     char* font;
2917     char* text;
2918     char* outline=0;
2919     RGBA fill;
2920     if(!strcmp(command, "circle"))
2921         type = 1;
2922     else if(!strcmp(command, "filled"))
2923         type = 2;
2924    
2925     if(type==0) {
2926         width = parseTwip(lu(args, "width"));
2927         height = parseTwip(lu(args, "height"));
2928     } else if (type==1) {
2929         r = parseTwip(lu(args, "r"));
2930     } else if (type==2) {
2931         outline = lu(args, "outline");
2932     }
2933
2934     if(!strcmp(fillstr, "fill"))
2935         fillstr = colorstr;
2936     if(!strcmp(fillstr, "none"))
2937         fillstr = 0;
2938     if(width<0 || height<0 || linewidth<0 || r<0)
2939         syntaxerror("values width, height, line, r must be positive");
2940     
2941     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2942     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2943     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2944     return 0;
2945 }
2946
2947 static int c_textshape(map_t*args) 
2948 {
2949     char*name = lu(args, "name");
2950     char*text = lu(args, "text");
2951     char*font = lu(args, "font");
2952     float size = parsePxOrPercent(font, lu(args, "size"));
2953
2954     s_textshape(name, font, size, text);
2955     return 0;
2956 }
2957
2958 static int c_swf(map_t*args) 
2959 {
2960     char*name = lu(args, "name");
2961     char*filename = lu(args, "filename");
2962     char*command = lu(args, "commandname");
2963     if(!strcmp(command, "shape"))
2964         warning("Please use .swf instead of .shape");
2965     s_includeswf(name, filename);
2966     return 0;
2967 }
2968
2969 static int c_font(map_t*args) 
2970 {
2971     char*name = lu(args, "name");
2972     char*filename = lu(args, "filename");
2973     s_font(name, filename);
2974     return 0;
2975 }
2976
2977 static int c_sound(map_t*args) 
2978 {
2979     char*name = lu(args, "name");
2980     char*filename = lu(args, "filename");
2981     s_sound(name, filename);
2982     return 0;
2983 }
2984
2985 static int c_text(map_t*args) 
2986 {
2987     char*name = lu(args, "name");
2988     char*text = lu(args, "text");
2989     char*font = lu(args, "font");
2990     float size = parsePxOrPercent(font, lu(args, "size"));
2991     RGBA color = parseColor(lu(args, "color"));
2992     s_text(name, font, text, (int)(size*100), color);
2993     return 0;
2994 }
2995
2996 static int c_soundtrack(map_t*args) 
2997 {
2998     return 0;
2999 }
3000
3001 static int c_quicktime(map_t*args) 
3002 {
3003     char*name = lu(args, "name");
3004     char*url = lu(args, "url");
3005     s_quicktime(name, url);
3006     return 0;
3007 }
3008
3009 static int c_image(map_t*args) 
3010 {
3011     char*command = lu(args, "commandname");
3012     char*name = lu(args, "name");
3013     char*filename = lu(args, "filename");
3014     if(!strcmp(command,"jpeg")) {
3015         int quality = (int)(parsePercent(lu(args, "quality"))*100);
3016         s_image(name, "jpeg", filename, quality);
3017     } else {
3018         s_image(name, "png", filename, 0);
3019     }
3020     return 0;
3021 }
3022
3023 static int c_outline(map_t*args) 
3024 {
3025     char*name = lu(args, "name");
3026     char*format = lu(args, "format");
3027
3028     readToken();
3029     if(type != RAWDATA)
3030         syntaxerror("colon (:) expected");
3031
3032     s_outline(name, format, text);
3033     return 0;
3034 }
3035
3036 int fakechar(map_t*args)
3037 {
3038     char*name = lu(args, "name");
3039     s_box(name, 0, 0, black, 20, 0);
3040     return 0;
3041 }
3042
3043 static int c_egon(map_t*args) {return fakechar(args);}
3044 static int c_button(map_t*args) {
3045     char*name = lu(args, "name");
3046     s_button(name);
3047     return 0;
3048 }
3049 static int current_button_flags = 0;
3050 static int c_on_press(map_t*args)
3051 {
3052     char*position = lu(args, "position");
3053     char*action = "";
3054     if(!strcmp(position, "inside")) {
3055         current_button_flags |= BC_OVERUP_OVERDOWN;
3056     } else if(!strcmp(position, "outside")) {
3057         //current_button_flags |= BC_IDLE_OUTDOWN;
3058         syntaxerror("IDLE_OVERDOWN not supported by SWF");
3059     } else if(!strcmp(position, "anywhere")) {
3060         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
3061     }
3062     readToken();
3063     if(type == RAWDATA) {
3064         action = text;
3065         s_buttonaction(current_button_flags, action);
3066         current_button_flags = 0;
3067     }
3068     else
3069         pushBack();
3070     return 0;
3071 }
3072 static int c_on_release(map_t*args)
3073 {
3074     char*position = lu(args, "position");
3075     char*action = "";
3076     if(!strcmp(position, "inside")) {
3077         current_button_flags |= BC_OVERDOWN_OVERUP;
3078     } else if(!strcmp(position, "outside")) {
3079         current_button_flags |= BC_OUTDOWN_IDLE;
3080     } else if(!strcmp(position, "anywhere")) {
3081         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
3082     }
3083     readToken();
3084     if(type == RAWDATA) {
3085         action = text;
3086         s_buttonaction(current_button_flags, action);
3087         current_button_flags = 0;
3088     }
3089     else
3090         pushBack();
3091     return 0;
3092 }
3093 static int c_on_move_in(map_t*args)
3094 {
3095     char*position = lu(args, "state");
3096     char*action = "";
3097     if(!strcmp(position, "pressed")) {
3098         current_button_flags |= BC_OUTDOWN_OVERDOWN;
3099     } else if(!strcmp(position, "not_pressed")) {
3100         current_button_flags |= BC_IDLE_OVERUP;
3101     } else if(!strcmp(position, "any")) {
3102         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
3103     }
3104     readToken();
3105     if(type == RAWDATA) {
3106         action = text;
3107         s_buttonaction(current_button_flags, action);
3108         current_button_flags = 0;
3109     }
3110     else
3111         pushBack();
3112     return 0;
3113 }
3114 static int c_on_move_out(map_t*args)
3115 {
3116     char*position = lu(args, "state");
3117     char*action = "";
3118     if(!strcmp(position, "pressed")) {
3119         current_button_flags |= BC_OVERDOWN_OUTDOWN;
3120     } else if(!strcmp(position, "not_pressed")) {
3121         current_button_flags |= BC_OVERUP_IDLE;
3122     } else if(!strcmp(position, "any")) {
3123         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
3124     }
3125     readToken();
3126     if(type == RAWDATA) {
3127         action = text;
3128         s_buttonaction(current_button_flags, action);
3129         current_button_flags = 0;
3130     }
3131     else
3132         pushBack();
3133     return 0;
3134 }
3135 static int c_on_key(map_t*args)
3136 {
3137     char*key = lu(args, "key");
3138     char*action = "";
3139     if(strlen(key)==1) {
3140         /* ascii */
3141         if(key[0]>=32) {
3142             current_button_flags |= 0x4000 + (key[0]*0x200);
3143         } else {
3144             syntaxerror("invalid character: %c"+key[0]);
3145             return 1;
3146         }
3147     } else {
3148         /* TODO: 
3149            <ctrl-x> = 0x200*(x-'a')
3150            esc = = 0x3600
3151            space = = 0x4000;
3152         */
3153         syntaxerror("invalid key: %s",key);
3154     }
3155     readToken();
3156     if(type == RAWDATA) {
3157         action = text;
3158         s_buttonaction(current_button_flags, action);
3159         current_button_flags = 0;
3160     }
3161     else
3162         pushBack();
3163     return 0;
3164 }
3165
3166 static int c_edittext(map_t*args) 
3167 {
3168  //"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"},
3169     char*name = lu(args, "name");
3170     char*font = lu(args, "font");
3171     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
3172     int width = parseTwip(lu(args, "width"));
3173     int height = parseTwip(lu(args, "height"));
3174     char*text  = lu(args, "text");
3175     RGBA color = parseColor(lu(args, "color"));
3176     int maxlength = parseInt(lu(args, "maxlength"));
3177     char*variable = lu(args, "variable");
3178     char*passwordstr = lu(args, "password");
3179     char*wordwrapstr = lu(args, "wordwrap");
3180     char*multilinestr = lu(args, "multiline");
3181     char*htmlstr = lu(args, "html");
3182     char*noselectstr = lu(args, "noselect");
3183     char*readonlystr = lu(args, "readonly");
3184     char*borderstr = lu(args, "border");
3185     char*autosizestr = lu(args, "autosize");
3186     char*alignstr = lu(args, "align");
3187     int align = -1;
3188
3189     int flags = 0;
3190     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
3191     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
3192     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
3193     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
3194     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
3195     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
3196     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
3197     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
3198     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
3199     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
3200     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
3201     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
3202     else syntaxerror("Unknown alignment: %s", alignstr);
3203
3204     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
3205     return 0;
3206 }
3207
3208 static int c_morphshape(map_t*args) {return fakechar(args);}
3209 static int c_movie(map_t*args) {return fakechar(args);}
3210
3211 static char* readfile(const char*filename)
3212 {
3213     FILE*fi = fopen(filename, "rb");
3214     int l;
3215     char*text;
3216     if(!fi) 
3217         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
3218     fseek(fi, 0, SEEK_END);
3219     l = ftell(fi);
3220     fseek(fi, 0, SEEK_SET);
3221     text = rfx_alloc(l+1);
3222     fread(text, l, 1, fi);
3223     text[l]=0;
3224     fclose(fi);
3225     return text;
3226 }
3227
3228 static int c_action(map_t*args) 
3229 {
3230     char* filename  = map_lookup(args, "filename");
3231     if(!filename ||!*filename) {
3232         readToken();
3233         if(type != RAWDATA) {
3234             syntaxerror("colon (:) expected");
3235         }
3236         s_action(text);
3237     } else {
3238         s_action(readfile(filename));
3239     }
3240    
3241     return 0;
3242 }
3243
3244 static int c_initaction(map_t*args) 
3245 {
3246     char* character = lu(args, "name");
3247     char* filename  = map_lookup(args, "filename");
3248     if(!filename ||!*filename) {
3249         readToken();
3250         if(type != RAWDATA) {
3251             syntaxerror("colon (:) expected");
3252         }
3253         s_initaction(character, text);
3254     } else {
3255         s_initaction(character, readfile(filename));
3256     }
3257    
3258     return 0;
3259 }
3260
3261 static struct {
3262     char*command;
3263     command_func_t* func;
3264     char*arguments;
3265 } arguments[] =
3266 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
3267  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
3268  // "import" type stuff
3269  {"swf", c_swf, "name filename"},
3270  {"shape", c_swf, "name filename"},
3271  {"jpeg", c_image, "name filename quality=80%"},
3272  {"png", c_image, "name filename"},
3273  {"movie", c_movie, "name filename"},
3274  {"sound", c_sound, "name filename"},
3275  {"font", c_font, "name filename"},
3276  {"soundtrack", c_soundtrack, "filename"},
3277  {"quicktime", c_quicktime, "url"},
3278
3279     // generators of primitives
3280
3281  {"point", c_point, "name x=0 y=0"},
3282  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
3283  {"outline", c_outline, "name format=simple"},
3284  {"textshape", c_textshape, "name font size=100% text"},
3285
3286     // filters
3287  {"blur", c_blur, "name blur= blurx= blury= passes=1"},
3288  {"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"},
3289  {"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"},
3290  {"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"},
3291
3292     // character generators
3293  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
3294  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
3295  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
3296
3297  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
3298  {"text", c_text, "name text font size=100% color=white"},
3299  {"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="},
3300  {"morphshape", c_morphshape, "name start end"},
3301  {"button", c_button, "name"},
3302     {"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="},
3303     {"on_press", c_on_press, "position=inside"},
3304     {"on_release", c_on_release, "position=anywhere"},
3305     {"on_move_in", c_on_move_in, "state=not_pressed"},
3306     {"on_move_out", c_on_move_out, "state=not_pressed"},
3307     {"on_key", c_on_key, "key=any"},
3308  
3309     // control tags
3310  {"play", c_play, "name loop=0 @nomultiple=0"},
3311  {"stop", c_stop, "name= "},
3312  {"nextframe", c_nextframe, "name"},
3313  {"previousframe", c_previousframe, "name"},
3314
3315     // object placement tags
3316  {"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="},
3317  {"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="},
3318  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3319  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3320  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3321  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3322  {"del", c_del, "name"},
3323     // virtual object placement
3324  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
3325
3326     // commands which start a block
3327 //startclip (see above)
3328  {"sprite", c_sprite, "name"},
3329  {"action", c_action, "filename="},
3330  {"initaction", c_initaction, "name filename="},
3331
3332  {"end", c_end, ""}
3333 };
3334
3335
3336 static map_t parseArguments(char*command, char*pattern)
3337 {
3338     char*x;
3339     char*d,*e;
3340    
3341     string_t name[64];
3342     string_t value[64];
3343     int set[64];
3344     int isboolean[64];
3345     int pos;
3346     int len;
3347     int t;
3348     string_t t1,t2;
3349     map_t result;
3350     map_init(&result);
3351
3352     string_set(&t1, "commandname");
3353     string_set(&t2, command);
3354     map_put(&result, t1, t2);
3355
3356     if(!pattern || !*pattern)
3357         return result;
3358
3359     x = pattern;
3360
3361     pos = 0;
3362
3363     if(!strncmp("<i> ", x, 3)) {
3364         readToken();
3365         if(type == COMMAND || type == RAWDATA) {
3366             pushBack();
3367             syntaxerror("character name expected");
3368         }
3369         name[pos].str = "instance";
3370         name[pos].len = 8;
3371         value[pos].str = text;
3372         value[pos].len = strlen(text);
3373         set[pos] = 1;
3374         pos++;
3375
3376         if(type == ASSIGNMENT)
3377             readToken();
3378
3379         name[pos].str = "character";
3380         name[pos].len = 9;
3381         value[pos].str = text;
3382         value[pos].len = strlen(text);
3383         set[pos] = 1;
3384         pos++;
3385
3386         x+=4;
3387     }
3388
3389     while(*x) {
3390         isboolean[pos] = (x[0] =='@');
3391         if(isboolean[pos])
3392             x++;
3393
3394         d = strchr(x, ' ');
3395         e = strchr(x, '=');
3396         if(!d)
3397             d=&x[strlen(x)];
3398         set[pos] = 0;
3399
3400         if(!e || d<e) { 
3401             // no default
3402             name[pos].str = x;
3403             name[pos].len = d-x;
3404             value[pos].str = 0;
3405             value[pos].len = 0;
3406         } else {
3407             name[pos].str = x;
3408             name[pos].len = e-x;
3409             value[pos].str = e+1;
3410             value[pos].len = d-e-1;
3411         }
3412         pos++;
3413         if(!*d) break;
3414         x=d+1;
3415     }
3416     len = pos;
3417
3418 /*    for(t=0;t<len;t++) {
3419         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3420                 isboolean[t]?"(boolean)":"");
3421     }*/
3422
3423     while(1) {
3424         readToken();
3425         if(type == RAWDATA || type == COMMAND) {
3426             pushBack();
3427             break;
3428         }
3429
3430         // first, search for boolean arguments
3431         for(pos=0;pos<len;pos++)
3432         {
3433             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3434                 set[pos] = 1;
3435                 if(type == ASSIGNMENT)
3436                     readToken();
3437                 value[pos].str = text;
3438                 value[pos].len = strlen(text);
3439                 /*printf("setting boolean parameter %s (to %s)\n",
3440                         strdup_n(name[pos], namelen[pos]),
3441                         strdup_n(value[pos], valuelen[pos]));*/
3442                 break;
3443             }
3444         }
3445
3446         // second, search for normal arguments
3447         if(pos==len)
3448         for(pos=0;pos<len;pos++)
3449         {
3450             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3451                (type != ASSIGNMENT && !set[pos])) {
3452                 if(set[pos]) {
3453                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3454                 }
3455                 if(type == ASSIGNMENT)
3456                     readToken();
3457                 set[pos] = 1;
3458                 value[pos].str = text;
3459                 value[pos].len = strlen(text);
3460 #if 0//def DEBUG
3461                 printf("setting parameter %s (to %s)\n",
3462                         strdup_n(name[pos].str, name[pos].len),
3463                         strdup_n(value[pos].str, value[pos].len));
3464 #endif
3465                 break;
3466             }
3467         }
3468         if(pos==len) {
3469             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3470         }
3471     }
3472 #if 0//def DEBUG
3473     for(t=0;t<len;t++) {
3474         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3475     }
3476 #endif
3477     for(t=0;t<len;t++) {
3478         if(value[t].str && value[t].str[0] == '*') {
3479             //relative default- take value from some other parameter
3480             int s;
3481             for(s=0;s<len;s++) {
3482                 if(value[s].len == value[t].len-1 &&
3483                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
3484                     value[t].str = value[s].str;
3485             }
3486         }
3487         if(value[t].str == 0) {
3488             pushBack();
3489             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3490         }
3491     }
3492
3493     /* ok, now construct the dictionary from the parameters */
3494
3495     for(t=0;t<len;t++) 
3496     {
3497         map_put(&result, name[t], value[t]);
3498     }
3499     return result;
3500 }
3501 static void parseArgumentsForCommand(char*command)
3502 {
3503     int t;
3504     map_t args;
3505     int nr = -1;
3506     msg("<verbose> parse Command: %s (line %d)", command, line);
3507
3508     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3509         if(!strcmp(arguments[t].command, command)) {
3510
3511             /* ugly hack- will be removed soon (once documentation and .sc generating
3512                utilities have been changed) */
3513             if(!strcmp(command, "swf") && !stackpos) {
3514                 warning("Please use .flash instead of .swf- this will be mandatory soon");      
3515                 command = "flash";
3516                 t = 0;
3517             }
3518
3519             args = parseArguments(command, arguments[t].arguments);
3520             nr = t;
3521             break;
3522         }
3523     }
3524     if(nr<0)
3525         syntaxerror("command %s not known", command);
3526    
3527     // catch missing .flash directives at the beginning of a file
3528     if(strcmp(command, "flash") && !stackpos)
3529     {
3530         syntaxerror("No movie defined- use .flash first");
3531     }
3532
3533 #ifdef DEBUG
3534     printf(".%s\n", command);fflush(stdout);
3535     map_dump(&args, stdout, "\t");fflush(stdout);
3536 #endif
3537
3538     (*arguments[nr].func)(&args);
3539
3540     /*if(!strcmp(command, "button") ||
3541        !strcmp(command, "action")) {
3542         while(1) {
3543             readToken();
3544             if(type == COMMAND) {
3545                 if(!strcmp(text, "end"))
3546                     break;
3547                 else {
3548                     pushBack();
3549                     break;
3550                 }
3551             }
3552         }
3553     }*/
3554
3555     map_clear(&args);
3556     return;
3557 }
3558
3559 int main (int argc,char ** argv)
3560
3561     int t;
3562     processargs(argc, argv);
3563     initLog(0,-1,0,0,-1,verbose);
3564
3565     if(!filename) {
3566         args_callback_usage(argv[0]);
3567         exit(1);
3568     }
3569     
3570     file = generateTokens(filename);
3571     if(!file) {
3572         fprintf(stderr, "parser returned error.\n");
3573         return 1;
3574     }
3575     pos=0;
3576     t=0;
3577
3578     while(!noMoreTokens()) {
3579         readToken();
3580         if(type != COMMAND)
3581             syntaxerror("command expected");
3582         parseArgumentsForCommand(text);
3583     }
3584
3585     s_close();
3586     freeTokens(file);
3587
3588     return 0;
3589 }
3590