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