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