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