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