added -C option
[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         /* We simply dump all tags right after the sprite
1524            header, relying on the fact that swf_OptimizeTagOrder() will
1525            sort things out for us later. 
1526            We also rely on the fact that the imported SWF is well-formed.
1527          */
1528         tag = swf_InsertTag(tag, ftag->id);
1529         swf_SetBlock(tag, ftag->data, ftag->len);
1530         ftag = ftag->next;
1531     }
1532     if(!ftag)
1533         syntaxerror("Included file %s contains errors", filename);
1534     tag = swf_InsertTag(tag, ST_END);
1535
1536     swf_FreeTags(&swf);
1537
1538     s_addcharacter(name, id, tag, r);
1539     incrementid();
1540 }
1541 SRECT s_getCharBBox(char*name)
1542 {
1543     character_t* c = dictionary_lookup(&characters, name);
1544     if(!c) syntaxerror("character '%s' unknown(2)", name);
1545     return c->size;
1546 }
1547 SRECT s_getInstanceBBox(char*name)
1548 {
1549     instance_t * i = dictionary_lookup(&instances, name);
1550     character_t * c;
1551     if(!i) syntaxerror("instance '%s' unknown(4)", name);
1552     c = i->character;
1553     if(!c) syntaxerror("internal error(5)");
1554     return c->size;
1555 }
1556 parameters_t s_getParameters(char*name)
1557 {
1558     instance_t * i = dictionary_lookup(&instances, name);
1559     if(!i) syntaxerror("instance '%s' unknown(10)", name);
1560     return i->parameters;
1561 }
1562 void s_startclip(char*instance, char*character, parameters_t p)
1563 {
1564     character_t* c = dictionary_lookup(&characters, character);
1565     instance_t* i;
1566     MATRIX m;
1567     if(!c) {
1568         syntaxerror("character %s not known", character);
1569     }
1570     i = s_addinstance(instance, c, currentdepth);
1571     i->parameters = p;
1572     m = s_instancepos(i->character->size, &p);
1573     
1574     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1575     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
1576     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1577     i->lastTag = tag;
1578     i->lastFrame= currentframe;
1579
1580     stack[stackpos].tag = tag;
1581     stack[stackpos].type = 2;
1582     stackpos++;
1583
1584     currentdepth++;
1585 }
1586 void s_endClip()
1587 {
1588     SWFPLACEOBJECT p;
1589     stackpos--;
1590     swf_SetTagPos(stack[stackpos].tag, 0);
1591     swf_GetPlaceObject(stack[stackpos].tag, &p);
1592     p.clipdepth = currentdepth;
1593     p.name = 0;
1594     swf_ClearTag(stack[stackpos].tag);
1595     swf_SetPlaceObject(stack[stackpos].tag, &p);
1596     currentdepth++;
1597 }
1598
1599 void s_put(char*instance, char*character, parameters_t p)
1600 {
1601     character_t* c = dictionary_lookup(&characters, character);
1602     instance_t* i;
1603     MATRIX m;
1604     if(!c) {
1605         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
1606     }
1607     
1608     i = s_addinstance(instance, c, currentdepth);
1609     i->parameters = p;
1610     m = s_instancepos(i->character->size, &p);
1611     
1612     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1613     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
1614     i->lastTag = tag;
1615     i->lastFrame = currentframe;
1616     currentdepth++;
1617 }
1618
1619 void s_jump(char*instance, parameters_t p)
1620 {
1621     instance_t* i = dictionary_lookup(&instances, instance);
1622     MATRIX m;
1623     if(!i) {
1624         syntaxerror("instance %s not known", instance);
1625     }
1626
1627     i->parameters = p;
1628     m = s_instancepos(i->character->size, &p);
1629
1630     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1631     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
1632     i->lastTag = tag;
1633     i->lastFrame = currentframe;
1634 }
1635
1636 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
1637 {
1638     parameters_t p;
1639     float ratio;
1640     if(num==0 || num==1)
1641         return *p1;
1642     ratio = (float)pos/(float)num;
1643     
1644     p.x = (p2->x-p1->x)*ratio + p1->x;
1645     p.y = (p2->y-p1->y)*ratio + p1->y;
1646     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
1647     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
1648     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
1649     p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
1650
1651     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
1652     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
1653     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
1654     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
1655
1656     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
1657     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
1658     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
1659     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
1660
1661     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
1662     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
1663     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
1664     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
1665     return p;
1666 }
1667
1668 void s_change(char*instance, parameters_t p2)
1669 {
1670     instance_t* i = dictionary_lookup(&instances, instance);
1671     MATRIX m;
1672     parameters_t p1;
1673     TAG*t;
1674     int frame, allframes;
1675     if(!i) {
1676         syntaxerror("instance %s not known", instance);
1677     }
1678     p1 = i->parameters;
1679     
1680     allframes = currentframe - i->lastFrame - 1;
1681     if(allframes < 0) {
1682         warning(".change ignored. can only .put/.change an object once per frame.");
1683         return;
1684     }
1685     
1686     m = s_instancepos(i->character->size, &p2);
1687     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1688     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
1689     i->parameters = p2;
1690
1691     /* o.k., we got the start and end point set. Now iterate though all the
1692        tags in between, inserting object changes after each new frame */
1693     t = i->lastTag;
1694     i->lastTag = tag;
1695     if(!t) syntaxerror("internal error(6)");
1696     frame = 0;
1697     while(frame < allframes) {
1698         if(t->id == ST_SHOWFRAME) {
1699             parameters_t p;
1700             MATRIX m;
1701             TAG*lt;
1702             frame ++;
1703             p = s_interpolate(&p1, &p2, frame, allframes);
1704             m = s_instancepos(i->character->size, &p); //needed?
1705             lt = swf_InsertTag(t, ST_PLACEOBJECT2);
1706             i->lastFrame = currentframe;
1707             swf_ObjectMove(lt, i->depth, &m, &p.cxform);
1708             t = lt;
1709             if(frame == allframes)
1710                 break;
1711         }
1712         t = t->next;
1713         if(!t) 
1714             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
1715     }
1716 }
1717
1718 void s_delinstance(char*instance)
1719 {
1720     instance_t* i = dictionary_lookup(&instances, instance);
1721     if(!i) {
1722         syntaxerror("instance %s not known", instance);
1723     }
1724     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
1725     swf_SetU16(tag, i->depth);
1726     dictionary_del(&instances, instance);
1727 }
1728
1729 void s_qchange(char*instance, parameters_t p)
1730 {
1731 }
1732
1733 void s_end()
1734 {
1735     if(!stackpos)
1736         syntaxerror(".end unexpected");
1737     if(stack[stackpos-1].type == 0)
1738         s_endSWF();
1739     else if(stack[stackpos-1].type == 1)
1740         s_endSprite();
1741     else if(stack[stackpos-1].type == 2)
1742         s_endClip();
1743     else if(stack[stackpos-1].type == 3)
1744         s_endButton();
1745     else syntaxerror("internal error 1");
1746 }
1747
1748 // ------------------------------------------------------------------------
1749
1750 typedef int command_func_t(map_t*args);
1751
1752 SRECT parseBox(char*str)
1753 {
1754     SRECT r;
1755     float xmin, xmax, ymin, ymax;
1756     char*x = strchr(str, 'x');
1757     char*d1=0,*d2=0;
1758     if(!strcmp(str, "autocrop")) {
1759         r.xmin = r.ymin = r.xmax = r.ymax = 0;
1760         return r;
1761     }
1762     if(!x) goto error;
1763     d1 = strchr(x+1, ':');
1764     if(d1)
1765         d2 = strchr(d1+1, ':');
1766     if(!d1) {
1767         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1768             goto error;
1769         xmin = ymin = 0;
1770     }
1771     else if(d1 && !d2) {
1772         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1773             goto error;
1774         xmax += xmin;
1775         ymin = 0;
1776     }
1777     else {
1778         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1779             goto error;
1780         xmax += xmin;
1781         ymax += ymin;
1782     }
1783     r.xmin = (SCOORD)(xmin*20);
1784     r.ymin = (SCOORD)(ymin*20);
1785     r.xmax = (SCOORD)(xmax*20);
1786     r.ymax = (SCOORD)(ymax*20);
1787     return r;
1788 error:
1789     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1790     return r;
1791 }
1792 float parseFloat(char*str)
1793 {
1794     return atof(str);
1795 }
1796 int parseInt(char*str)
1797 {
1798     int t;
1799     int l=strlen(str);
1800     int s=0;
1801     if(str[0]=='+' || str[0]=='-')
1802         s++;
1803
1804     for(t=s;t<l;t++)
1805         if(str[t]<'0' || str[t]>'9')
1806             syntaxerror("Not an Integer: \"%s\"", str);
1807     return atoi(str);
1808 }
1809 int parseTwip(char*str)
1810 {
1811     char*dot;
1812     int sign=1;
1813     if(str[0]=='+' || str[0]=='-') {
1814         if(str[0]=='-')
1815             sign = -1;
1816         str++;
1817     }
1818     dot = strchr(str, '.');
1819     if(!dot) {
1820         int l=strlen(str);
1821         int t;
1822         return sign*parseInt(str)*20;
1823     } else {
1824         char* old = strdup(str);
1825         int l=strlen(dot+1);
1826         char*s;
1827         *dot++ = 0;
1828         for(s=str;s<dot-1;s++)
1829             if(*s<'0' || *s>'9')
1830                 syntaxerror("Not a coordinate: \"%s\"", str);
1831         for(s=dot;*s;s++) {
1832             if(*s<'0' || *s>'9')
1833                 syntaxerror("Not a coordinate: \"%s\"", str);
1834         }
1835         if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
1836             dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
1837             dot[2] = 0;
1838             l=2;
1839             warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
1840         }
1841         free(old);
1842         if(l==0)
1843             return sign*atoi(str)*20;
1844         if(l==1)
1845             return sign*atoi(str)*20+atoi(dot)*2;
1846         if(l==2)
1847             return sign*atoi(str)*20+atoi(dot)/5;
1848     }
1849     return 0;
1850 }
1851
1852 int isPoint(char*str)
1853 {
1854     if(strchr(str, '('))
1855         return 1;
1856     else
1857         return 0;
1858 }
1859
1860 SPOINT parsePoint(char*str)
1861 {
1862     SPOINT p;
1863     char tmp[80];
1864     int l = strlen(str);
1865     char*comma = strchr(str, ',');
1866     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1867         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1868     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1869     p.x = parseTwip(tmp);
1870     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1871     p.y = parseTwip(tmp);
1872     return p;
1873 }
1874
1875 int parseColor2(char*str, RGBA*color)
1876 {
1877     int l = strlen(str);
1878     int r,g,b,a;
1879     int t;
1880
1881     struct {unsigned char r,g,b;char*name;} colors[] =
1882     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1883     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1884     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1885     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1886     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1887     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1888     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1889     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1890     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1891     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1892     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1893     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1894
1895     a=255;r=g=b=0;
1896
1897     if(str[0]=='#' && (l==7 || l==9)) {
1898         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1899             return 0;
1900         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1901             return 0;
1902         color->r = r; color->g = g; color->b = b; color->a = a;
1903         return 1;
1904     }
1905     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1906         if(!strcmp(str, colors[t].name)) {
1907             r = colors[t].r;
1908             g = colors[t].g;
1909             b = colors[t].b;
1910             a = 255;
1911             color->r = r; color->g = g; color->b = b; color->a = a;
1912             return 1;
1913         }
1914     return 0;
1915
1916 }
1917 RGBA parseColor(char*str)
1918 {
1919     RGBA c;
1920     if(!parseColor2(str, &c))
1921         syntaxerror("Expression '%s' is not a color", str);
1922     return c;
1923 }
1924
1925 typedef struct _muladd {
1926     S16 mul;
1927     S16 add;
1928 } MULADD;
1929
1930 MULADD parseMulAdd(char*str)
1931 {
1932     float add, mul;
1933     char* str2 = (char*)malloc(strlen(str)+5);
1934     int i;
1935     MULADD m;
1936     strcpy(str2, str);
1937     strcat(str2, " 0");
1938     add = 0;
1939     mul = 1.0;
1940     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1941     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1942     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1943     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1944     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1945     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1946     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1947     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1948     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1949     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1950     else {
1951         syntaxerror("'%s' is not a valid color transform expression", str);
1952     }
1953     m.add = (int)(add*256);
1954     m.mul = (int)(mul*256);
1955     free(str2);
1956     return m;
1957 }
1958
1959 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1960 {
1961     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1962     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1963     MULADD r;
1964     if(a<-32768) a=-32768;
1965     if(a>32767) a=32767;
1966     if(m<-32768) m=-32768;
1967     if(m>32767) m=32767;
1968     r.add = a;
1969     r.mul = (int)m;
1970     return r;
1971 }
1972
1973 float parsePxOrPercent(char*fontname, char*str)
1974 {
1975     int l = strlen(str);
1976     if(strchr(str, '%'))
1977         return parsePercent(str);
1978     if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
1979         float p = atof(str);
1980         return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
1981     }
1982     syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
1983     return 0;
1984 }
1985
1986 float parsePercent(char*str)
1987 {
1988     int l = strlen(str);
1989     if(!l)
1990         return 1.0;
1991     if(str[l-1]=='%') {
1992         return atoi(str)/100.0;
1993     }
1994     syntaxerror("Expression '%s' is not a percentage", str);
1995     return 0;
1996 }
1997 int isPercent(char*str)
1998 {
1999     return str[strlen(str)-1]=='%';
2000 }
2001 int parseNewSize(char*str, int size)
2002 {
2003     if(isPercent(str))
2004         return parsePercent(str)*size;
2005     else
2006         return (int)(atof(str)*20);
2007 }
2008
2009 int isColor(char*str)
2010 {
2011     RGBA c;
2012     return parseColor2(str, &c);
2013 }
2014
2015 static char* lu(map_t* args, char*name)
2016 {
2017     char* value = map_lookup(args, name);
2018     if(!value) {
2019         map_dump(args, stdout, "");
2020         syntaxerror("internal error 2: value %s should be set", name);
2021     }
2022     return value;
2023 }
2024
2025 static int c_flash(map_t*args) 
2026 {
2027     char* filename = map_lookup(args, "filename");
2028     char* compressstr = lu(args, "compress");
2029     SRECT bbox = parseBox(lu(args, "bbox"));
2030     int version = parseInt(lu(args, "version"));
2031     int fps = (int)(parseFloat(lu(args, "fps"))*256);
2032     int compress = 0;
2033     RGBA color = parseColor(lu(args, "background"));
2034
2035     if(!filename || !*filename) {
2036         /* for compatibility */
2037         filename = map_lookup(args, "name");
2038         if(!filename || !*filename) {
2039             filename = 0;
2040         } else {
2041             //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2042             msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2043         }
2044     }
2045
2046     if(!filename || override_outputname)
2047         filename = outputname;
2048     
2049     if(!strcmp(compressstr, "default"))
2050         compress = version==6;
2051     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2052         compress = 1;
2053     else if(!strcmp(compressstr, "no"))
2054         compress = 0;
2055     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2056
2057     s_swf(filename, bbox, version, fps, compress, color);
2058     return 0;
2059 }
2060 int isRelative(char*str)
2061 {
2062     return !strncmp(str, "<plus>", 6) ||
2063            !strncmp(str, "<minus>", 7);
2064 }
2065 char* getOffset(char*str)
2066 {
2067     if(!strncmp(str, "<plus>", 6))
2068         return str+6;
2069     if(!strncmp(str, "<minus>", 7))
2070         return str+7;
2071     syntaxerror("internal error (347)");
2072     return 0;
2073 }
2074 int getSign(char*str)
2075 {
2076     if(!strncmp(str, "<plus>", 6))
2077         return 1;
2078     if(!strncmp(str, "<minus>", 7))
2079         return -1;
2080     syntaxerror("internal error (348)");
2081     return 0;
2082 }
2083 static dictionary_t points;
2084 static mem_t mpoints;
2085 int points_initialized = 0;
2086
2087 SPOINT getPoint(SRECT r, char*name)
2088 {
2089     int l=0;
2090     if(!strcmp(name, "center")) {
2091         SPOINT p;
2092         p.x = (r.xmin + r.xmax)/2;
2093         p.y = (r.ymin + r.ymax)/2;
2094         return p;
2095     }
2096
2097     if(points_initialized)
2098         l = (int)dictionary_lookup(&points, name);
2099     if(l==0) {
2100         syntaxerror("Invalid point: \"%s\".", name);
2101     }
2102     l--;
2103     return *(SPOINT*)&mpoints.buffer[l];
2104 }
2105
2106 static int texture2(char*name, char*object, map_t*args, int errors) 
2107 {
2108     SPOINT pos,size;
2109     char*xstr = map_lookup(args, "x");
2110     char*ystr = map_lookup(args, "y");
2111     char*widthstr = map_lookup(args, "width");
2112     char*heightstr = map_lookup(args, "height");
2113     char*scalestr = map_lookup(args, "scale");
2114     char*scalexstr = map_lookup(args, "scalex");
2115     char*scaleystr = map_lookup(args, "scaley");
2116     char*rotatestr = map_lookup(args, "rotate");
2117     char* shearstr = map_lookup(args, "shear");
2118     char* radiusstr = map_lookup(args, "r");
2119     float x=0,y=0;
2120     float scalex = 1.0, scaley = 1.0;
2121     float rotate=0, shear=0;
2122     float r = 0;
2123     if(!*xstr && !*ystr) {
2124         if(errors)
2125             syntaxerror("x and y must be set");
2126         return 0;
2127     }
2128     if(*scalestr && (*scalexstr || *scaleystr)) {
2129         syntaxerror("scale and scalex/scaley can't both be set");
2130         return 0;
2131     }
2132     if((*widthstr || *heightstr) && *radiusstr) {
2133         syntaxerror("width/height and radius can't both be set");
2134     }
2135     if(*radiusstr) {
2136         widthstr = radiusstr;
2137         heightstr = radiusstr;
2138     }
2139     if(!*xstr) xstr="0";
2140     if(!*ystr) ystr="0";
2141     if(!*rotatestr) rotatestr="0";
2142     if(!*shearstr) shearstr="0";
2143
2144     if(*scalestr) {
2145         scalex = scaley = parsePercent(scalestr);
2146     } else if(*scalexstr || *scaleystr) {
2147         if(scalexstr) scalex = parsePercent(scalexstr);
2148         if(scaleystr) scaley = parsePercent(scaleystr);
2149     } else if(*widthstr || *heightstr) {
2150         int width=0;
2151         int height=0;
2152         s_getBitmapSize(object, &width, &height);
2153         if(*widthstr)
2154             scalex = (float)parseTwip(widthstr)/(float)width;
2155         if(*heightstr)
2156             scaley = (float)parseTwip(heightstr)/(float)height;
2157     }
2158     x = parseTwip(xstr);
2159     y = parseTwip(ystr);
2160     rotate = parseFloat(rotatestr);
2161     shear = parseFloat(shearstr);
2162
2163     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2164
2165     return 0;
2166 }
2167
2168 static int c_texture(map_t*args) 
2169 {
2170     char*name = lu(args, "instance");
2171     char*object = lu(args, "character");
2172     return texture2(name, object, args, 1);
2173 }
2174
2175 static int c_gradient(map_t*args) 
2176 {
2177     char*name = lu(args, "name");
2178     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2179     int rotate = parseInt(lu(args, "rotate"));
2180
2181     readToken();
2182     if(type != RAWDATA)
2183         syntaxerror("colon (:) expected");
2184
2185     s_gradient(name, text, radial, rotate);
2186
2187     /* check whether we also have placement information,
2188        which would make this a positioned gradient.
2189        If there is placement information, texture2() will
2190        add a texture, which has priority over the gradient.
2191      */
2192     texture2(name, name, args, 0);
2193     return 0;
2194 }
2195 static int c_point(map_t*args) 
2196 {
2197     char*name = lu(args, "name");
2198     int pos;
2199     string_t s1;
2200     SPOINT p;
2201     if(!points_initialized) {
2202         dictionary_init(&points);
2203         mem_init(&mpoints);
2204         points_initialized = 1;
2205     }
2206     p.x = parseTwip(lu(args, "x"));
2207     p.y = parseTwip(lu(args, "y"));
2208     pos = mem_put(&mpoints, &p, sizeof(p));
2209     string_set(&s1, name);
2210     pos++;
2211     dictionary_put(&points, s1, (void*)pos);
2212     return 0;
2213 }
2214 static int c_play(map_t*args) 
2215 {
2216     char*name = lu(args, "name");
2217     char*loop = lu(args, "loop");
2218     char*nomultiple = lu(args, "nomultiple");
2219     int nm = 0;
2220     if(!strcmp(nomultiple, "nomultiple"))
2221         nm = 1;
2222     else
2223         nm = parseInt(nomultiple);
2224
2225     if(s_playsound(name, parseInt(loop), nm, 0)) {
2226         return 0;
2227     } else if(s_swf3action(name, "play")) {
2228         return 0;
2229     }
2230     return 0;
2231 }
2232
2233 static int c_stop(map_t*args) 
2234 {
2235     char*name = map_lookup(args, "name");
2236
2237     if(s_playsound(name, 0,0,1)) {
2238         return 0;
2239     } else if(s_swf3action(name, "stop")) {
2240         return 0;
2241     }
2242     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2243     return 0;
2244 }
2245
2246 static int c_nextframe(map_t*args) 
2247 {
2248     char*name = lu(args, "name");
2249
2250     if(s_swf3action(name, "nextframe")) {
2251         return 0;
2252     }
2253     syntaxerror("I don't know anything about movie \"%s\"", name);
2254     return 0;
2255 }
2256
2257 static int c_previousframe(map_t*args) 
2258 {
2259     char*name = lu(args, "name");
2260
2261     if(s_swf3action(name, "previousframe")) {
2262         return 0;
2263     }
2264     syntaxerror("I don't know anything about movie \"%s\"", name);
2265     return 0;
2266 }
2267
2268 static int c_placement(map_t*args, int type)
2269 {
2270     char*instance = lu(args, (type==0||type==4)?"instance":"name");
2271     char*character = 0;
2272
2273     char* luminancestr = lu(args, "luminance");
2274     char* scalestr = lu(args, "scale");
2275     char* scalexstr = lu(args, "scalex");
2276     char* scaleystr = lu(args, "scaley");
2277     char* rotatestr = lu(args, "rotate");
2278     char* shearstr = lu(args, "shear");
2279     char* xstr="", *pivotstr="";
2280     char* ystr="", *anglestr="";
2281     char*above = lu(args, "above"); /*FIXME*/
2282     char*below = lu(args, "below");
2283     char* rstr = lu(args, "red");
2284     char* gstr = lu(args, "green");
2285     char* bstr = lu(args, "blue");
2286     char* astr = lu(args, "alpha");
2287     char* pinstr = lu(args, "pin");
2288     char* as = map_lookup(args, "as");
2289     MULADD r,g,b,a;
2290     float oldwidth;
2291     float oldheight;
2292     SRECT oldbbox;
2293     MULADD luminance;
2294     parameters_t p;
2295
2296     if(type==9) { // (?) .rotate  or .arcchange
2297         pivotstr = lu(args, "pivot");
2298         anglestr = lu(args, "angle");
2299     } else {
2300         xstr = lu(args, "x");
2301         ystr = lu(args, "y");
2302     }
2303     if(luminancestr[0])
2304         luminance = parseMulAdd(luminancestr);
2305     else {
2306         luminance.add = 0;
2307         luminance.mul = 256;
2308     }
2309
2310     if(scalestr[0]) {
2311         if(scalexstr[0]||scaleystr[0])
2312             syntaxerror("scalex/scaley and scale cannot both be set");
2313         scalexstr = scaleystr = scalestr;
2314     }
2315     
2316     if(type == 0 || type == 4)  {
2317         // put or startclip
2318         character = lu(args, "character");
2319         parameters_clear(&p);
2320     } else if (type == 5) {
2321         character = lu(args, "name");
2322         parameters_clear(&p);
2323         // button's show
2324     } else {
2325         p = s_getParameters(instance);
2326     }
2327
2328     /* x,y position */
2329     if(xstr[0]) {
2330         if(isRelative(xstr)) {
2331             if(type == 0 || type == 4)
2332                 syntaxerror("relative x values not allowed for initial put or startclip");
2333             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
2334         } else {
2335             p.x = parseTwip(xstr);
2336         }
2337     }
2338     if(ystr[0]) {
2339         if(isRelative(ystr)) {
2340             if(type == 0 || type == 4)
2341                 syntaxerror("relative y values not allowed for initial put or startclip");
2342             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
2343         } else {
2344             p.y = parseTwip(ystr);
2345         }
2346     }
2347
2348     /* scale, scalex, scaley */
2349     if(character) {
2350         oldbbox = s_getCharBBox(character);
2351     } else {
2352         oldbbox = s_getInstanceBBox(instance);
2353     }
2354     oldwidth = oldbbox.xmax - oldbbox.xmin;
2355     oldheight = oldbbox.ymax - oldbbox.ymin;
2356     if(scalexstr[0]) {
2357         if(oldwidth==0) p.scalex = 1.0;
2358         else {      
2359             if(scalexstr[0])
2360                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
2361         }
2362     }
2363     if(scaleystr[0]) {
2364         if(oldheight==0) p.scaley = 1.0;
2365         else {
2366             if(scaleystr[0])
2367                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
2368         }
2369     }
2370    
2371     /* rotation */
2372     if(rotatestr[0]) {
2373         if(isRelative(rotatestr)) {
2374             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
2375         } else {
2376             p.rotate = parseFloat(rotatestr);
2377         }
2378     }
2379
2380     /* shearing */
2381     if(shearstr[0]) {
2382         if(isRelative(shearstr)) {
2383             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
2384         } else {
2385             p.shear = parseFloat(shearstr);
2386         }
2387     }
2388
2389     if(pivotstr[0]) {
2390         if(isPoint(pivotstr)) 
2391             p.pivot = parsePoint(pivotstr);
2392         else 
2393             p.pivot = getPoint(oldbbox, pivotstr);
2394     }
2395     if(pinstr[0]) {
2396         if(isPoint(pinstr))
2397             p.pin = parsePoint(pinstr);
2398         else
2399             p.pin = getPoint(oldbbox, pinstr);
2400     }
2401         
2402     /* color transform */
2403
2404     if(rstr[0] || luminancestr[0]) {
2405         MULADD r;
2406         if(rstr[0])
2407             r = parseMulAdd(rstr);
2408         else {
2409             r.add = p.cxform.r0;
2410             r.mul = p.cxform.r1;
2411         }
2412         r = mergeMulAdd(r, luminance);
2413         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
2414     }
2415     if(gstr[0] || luminancestr[0]) {
2416         MULADD g;
2417         if(gstr[0])
2418             g = parseMulAdd(gstr);
2419         else {
2420             g.add = p.cxform.g0;
2421             g.mul = p.cxform.g1;
2422         }
2423         g = mergeMulAdd(g, luminance);
2424         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
2425     }
2426     if(bstr[0] || luminancestr[0]) {
2427         MULADD b;
2428         if(bstr[0])
2429             b = parseMulAdd(bstr);
2430         else {
2431             b.add = p.cxform.b0;
2432             b.mul = p.cxform.b1;
2433         }
2434         b = mergeMulAdd(b, luminance);
2435         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
2436     }
2437     if(astr[0]) {
2438         MULADD a = parseMulAdd(astr);
2439         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
2440     }
2441
2442     if(type == 0)
2443         s_put(instance, character, p);
2444     else if(type == 1)
2445         s_change(instance, p);
2446     else if(type == 2)
2447         s_qchange(instance, p);
2448     else if(type == 3)
2449         s_jump(instance, p);
2450     else if(type == 4)
2451         s_startclip(instance, character, p);
2452     else if(type == 5) {
2453         if(as && as[0]) {
2454             s_buttonput(character, as, p);
2455         } else {
2456             s_buttonput(character, "shape", p);
2457         }
2458     }
2459     return 0;
2460 }
2461 static int c_put(map_t*args) 
2462 {
2463     c_placement(args, 0);
2464     return 0;
2465 }
2466 static int c_change(map_t*args) 
2467 {
2468     c_placement(args, 1);
2469     return 0;
2470 }
2471 static int c_qchange(map_t*args) 
2472 {
2473     c_placement(args, 2);
2474     return 0;
2475 }
2476 static int c_arcchange(map_t*args) 
2477 {
2478     c_placement(args, 2);
2479     return 0;
2480 }
2481 static int c_jump(map_t*args) 
2482 {
2483     c_placement(args, 3);
2484     return 0;
2485 }
2486 static int c_startclip(map_t*args) 
2487 {
2488     c_placement(args, 4);
2489     return 0;
2490 }
2491 static int c_show(map_t*args) 
2492 {
2493     c_placement(args, 5);
2494     return 0;
2495 }
2496 static int c_del(map_t*args) 
2497 {
2498     char*instance = lu(args, "name");
2499     s_delinstance(instance);
2500     return 0;
2501 }
2502 static int c_end(map_t*args) 
2503 {
2504     s_end();
2505     return 0;
2506 }
2507 static int c_sprite(map_t*args) 
2508 {
2509     char* name = lu(args, "name");
2510     s_sprite(name);
2511     return 0;
2512 }
2513 static int c_frame(map_t*args) 
2514 {
2515     char*framestr = lu(args, "n");
2516     char*cutstr = lu(args, "cut");
2517     
2518     char*name = lu(args, "name");
2519     char*anchor = lu(args, "anchor");
2520     char buf[40];
2521
2522     if(!strcmp(anchor, "anchor") && !*name)
2523         name = framestr;
2524
2525     int frame;
2526     int cut = 0;
2527     if(strcmp(cutstr, "no"))
2528         cut = 1;
2529     if(isRelative(framestr)) {
2530         frame = s_getframe();
2531         if(getSign(framestr)<0)
2532             syntaxerror("relative frame expressions must be positive");
2533         frame += parseInt(getOffset(framestr));
2534     }
2535     else {
2536         frame = parseInt(framestr);
2537         if(s_getframe() >= frame
2538                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
2539             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
2540     }
2541     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
2542     return 0;
2543 }
2544 static int c_primitive(map_t*args) 
2545 {
2546     char*name = lu(args, "name");
2547     char*command = lu(args, "commandname");
2548     int width=0, height=0, r=0;
2549     int linewidth = parseTwip(lu(args, "line"));
2550     char*colorstr = lu(args, "color");
2551     RGBA color = parseColor(colorstr);
2552     char*fillstr = lu(args, "fill");
2553     int dofill = 1;
2554     int type=0;
2555     char* font;
2556     char* text;
2557     char* outline=0;
2558     RGBA fill;
2559     if(!strcmp(command, "circle"))
2560         type = 1;
2561     else if(!strcmp(command, "filled"))
2562         type = 2;
2563    
2564     if(type==0) {
2565         width = parseTwip(lu(args, "width"));
2566         height = parseTwip(lu(args, "height"));
2567     } else if (type==1) {
2568         r = parseTwip(lu(args, "r"));
2569     } else if (type==2) {
2570         outline = lu(args, "outline");
2571     }
2572
2573     if(!strcmp(fillstr, "fill"))
2574         fillstr = colorstr;
2575     if(!strcmp(fillstr, "none"))
2576         fillstr = 0;
2577     if(width<0 || height<0 || linewidth<0 || r<0)
2578         syntaxerror("values width, height, line, r must be positive");
2579     
2580     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
2581     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
2582     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
2583     return 0;
2584 }
2585
2586 static int c_textshape(map_t*args) 
2587 {
2588     char*name = lu(args, "name");
2589     char*text = lu(args, "text");
2590     char*font = lu(args, "font");
2591     float size = parsePxOrPercent(font, lu(args, "size"));
2592
2593     s_textshape(name, font, size, text);
2594     return 0;
2595 }
2596
2597 static int c_swf(map_t*args) 
2598 {
2599     char*name = lu(args, "name");
2600     char*filename = lu(args, "filename");
2601     char*command = lu(args, "commandname");
2602     if(!strcmp(command, "shape"))
2603         warning("Please use .swf instead of .shape");
2604     s_includeswf(name, filename);
2605     return 0;
2606 }
2607
2608 static int c_font(map_t*args) 
2609 {
2610     char*name = lu(args, "name");
2611     char*filename = lu(args, "filename");
2612     s_font(name, filename);
2613     return 0;
2614 }
2615
2616 static int c_sound(map_t*args) 
2617 {
2618     char*name = lu(args, "name");
2619     char*filename = lu(args, "filename");
2620     s_sound(name, filename);
2621     return 0;
2622 }
2623
2624 static int c_text(map_t*args) 
2625 {
2626     char*name = lu(args, "name");
2627     char*text = lu(args, "text");
2628     char*font = lu(args, "font");
2629     float size = parsePxOrPercent(font, lu(args, "size"));
2630     RGBA color = parseColor(lu(args, "color"));
2631     s_text(name, font, text, (int)(size*100), color);
2632     return 0;
2633 }
2634
2635 static int c_soundtrack(map_t*args) 
2636 {
2637     return 0;
2638 }
2639
2640 static int c_quicktime(map_t*args) 
2641 {
2642     char*name = lu(args, "name");
2643     char*url = lu(args, "url");
2644     s_quicktime(name, url);
2645     return 0;
2646 }
2647
2648 static int c_image(map_t*args) 
2649 {
2650     char*command = lu(args, "commandname");
2651     char*name = lu(args, "name");
2652     char*filename = lu(args, "filename");
2653     if(!strcmp(command,"jpeg")) {
2654         int quality = (int)(parsePercent(lu(args, "quality"))*100);
2655         s_image(name, "jpeg", filename, quality);
2656     } else {
2657         s_image(name, "png", filename, 0);
2658     }
2659     return 0;
2660 }
2661
2662 static int c_outline(map_t*args) 
2663 {
2664     char*name = lu(args, "name");
2665     char*format = lu(args, "format");
2666
2667     readToken();
2668     if(type != RAWDATA)
2669         syntaxerror("colon (:) expected");
2670
2671     s_outline(name, format, text);
2672     return 0;
2673 }
2674
2675 int fakechar(map_t*args)
2676 {
2677     char*name = lu(args, "name");
2678     s_box(name, 0, 0, black, 20, 0);
2679     return 0;
2680 }
2681
2682 static int c_egon(map_t*args) {return fakechar(args);}
2683 static int c_button(map_t*args) {
2684     char*name = lu(args, "name");
2685     s_button(name);
2686     return 0;
2687 }
2688 static int current_button_flags = 0;
2689 static int c_on_press(map_t*args)
2690 {
2691     char*position = lu(args, "position");
2692     char*action = "";
2693     if(!strcmp(position, "inside")) {
2694         current_button_flags |= BC_OVERUP_OVERDOWN;
2695     } else if(!strcmp(position, "outside")) {
2696         //current_button_flags |= BC_IDLE_OUTDOWN;
2697         syntaxerror("IDLE_OVERDOWN not supported by SWF");
2698     } else if(!strcmp(position, "anywhere")) {
2699         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
2700     }
2701     readToken();
2702     if(type == RAWDATA) {
2703         action = text;
2704         s_buttonaction(current_button_flags, action);
2705         current_button_flags = 0;
2706     }
2707     else
2708         pushBack();
2709     return 0;
2710 }
2711 static int c_on_release(map_t*args)
2712 {
2713     char*position = lu(args, "position");
2714     char*action = "";
2715     if(!strcmp(position, "inside")) {
2716         current_button_flags |= BC_OVERDOWN_OVERUP;
2717     } else if(!strcmp(position, "outside")) {
2718         current_button_flags |= BC_OUTDOWN_IDLE;
2719     } else if(!strcmp(position, "anywhere")) {
2720         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
2721     }
2722     readToken();
2723     if(type == RAWDATA) {
2724         action = text;
2725         s_buttonaction(current_button_flags, action);
2726         current_button_flags = 0;
2727     }
2728     else
2729         pushBack();
2730     return 0;
2731 }
2732 static int c_on_move_in(map_t*args)
2733 {
2734     char*position = lu(args, "state");
2735     char*action = "";
2736     if(!strcmp(position, "pressed")) {
2737         current_button_flags |= BC_OUTDOWN_OVERDOWN;
2738     } else if(!strcmp(position, "not_pressed")) {
2739         current_button_flags |= BC_IDLE_OVERUP;
2740     } else if(!strcmp(position, "any")) {
2741         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
2742     }
2743     readToken();
2744     if(type == RAWDATA) {
2745         action = text;
2746         s_buttonaction(current_button_flags, action);
2747         current_button_flags = 0;
2748     }
2749     else
2750         pushBack();
2751     return 0;
2752 }
2753 static int c_on_move_out(map_t*args)
2754 {
2755     char*position = lu(args, "state");
2756     char*action = "";
2757     if(!strcmp(position, "pressed")) {
2758         current_button_flags |= BC_OVERDOWN_OUTDOWN;
2759     } else if(!strcmp(position, "not_pressed")) {
2760         current_button_flags |= BC_OVERUP_IDLE;
2761     } else if(!strcmp(position, "any")) {
2762         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
2763     }
2764     readToken();
2765     if(type == RAWDATA) {
2766         action = text;
2767         s_buttonaction(current_button_flags, action);
2768         current_button_flags = 0;
2769     }
2770     else
2771         pushBack();
2772     return 0;
2773 }
2774 static int c_on_key(map_t*args)
2775 {
2776     char*key = lu(args, "key");
2777     char*action = "";
2778     if(strlen(key)==1) {
2779         /* ascii */
2780         if(key[0]>=32) {
2781             current_button_flags |= 0x4000 + (key[0]*0x200);
2782         } else {
2783             syntaxerror("invalid character: %c"+key[0]);
2784             return 1;
2785         }
2786     } else {
2787         /* TODO: 
2788            <ctrl-x> = 0x200*(x-'a')
2789            esc = = 0x3600
2790            space = = 0x4000;
2791         */
2792         syntaxerror("invalid key: %s",key);
2793     }
2794     readToken();
2795     if(type == RAWDATA) {
2796         action = text;
2797         s_buttonaction(current_button_flags, action);
2798         current_button_flags = 0;
2799     }
2800     else
2801         pushBack();
2802     return 0;
2803 }
2804
2805 static int c_edittext(map_t*args) 
2806 {
2807  //"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"},
2808     char*name = lu(args, "name");
2809     char*font = lu(args, "font");
2810     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
2811     int width = parseTwip(lu(args, "width"));
2812     int height = parseTwip(lu(args, "height"));
2813     char*text  = lu(args, "text");
2814     RGBA color = parseColor(lu(args, "color"));
2815     int maxlength = parseInt(lu(args, "maxlength"));
2816     char*variable = lu(args, "variable");
2817     char*passwordstr = lu(args, "password");
2818     char*wordwrapstr = lu(args, "wordwrap");
2819     char*multilinestr = lu(args, "multiline");
2820     char*htmlstr = lu(args, "html");
2821     char*noselectstr = lu(args, "noselect");
2822     char*readonlystr = lu(args, "readonly");
2823     char*borderstr = lu(args, "border");
2824     char*autosizestr = lu(args, "autosize");
2825     char*alignstr = lu(args, "align");
2826     int align = -1;
2827
2828     int flags = 0;
2829     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
2830     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
2831     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
2832     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
2833     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
2834     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
2835     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
2836     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
2837     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
2838     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
2839     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
2840     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
2841     else syntaxerror("Unknown alignment: %s", alignstr);
2842
2843     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
2844     return 0;
2845 }
2846
2847 static int c_morphshape(map_t*args) {return fakechar(args);}
2848 static int c_movie(map_t*args) {return fakechar(args);}
2849
2850 static char* readfile(const char*filename)
2851 {
2852     FILE*fi = fopen(filename, "rb");
2853     int l;
2854     char*text;
2855     if(!fi) 
2856         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
2857     fseek(fi, 0, SEEK_END);
2858     l = ftell(fi);
2859     fseek(fi, 0, SEEK_SET);
2860     text = rfx_alloc(l+1);
2861     fread(text, l, 1, fi);
2862     text[l]=0;
2863     fclose(fi);
2864     return text;
2865 }
2866
2867 static int c_action(map_t*args) 
2868 {
2869     char* filename  = map_lookup(args, "filename");
2870     if(!filename ||!*filename) {
2871         readToken();
2872         if(type != RAWDATA) {
2873             syntaxerror("colon (:) expected");
2874         }
2875         s_action(text);
2876     } else {
2877         s_action(readfile(filename));
2878     }
2879    
2880     return 0;
2881 }
2882
2883 static int c_initaction(map_t*args) 
2884 {
2885     char* character = lu(args, "name");
2886     char* filename  = map_lookup(args, "filename");
2887     if(!filename ||!*filename) {
2888         readToken();
2889         if(type != RAWDATA) {
2890             syntaxerror("colon (:) expected");
2891         }
2892         s_initaction(character, text);
2893     } else {
2894         s_initaction(character, readfile(filename));
2895     }
2896    
2897     return 0;
2898 }
2899
2900 static struct {
2901     char*command;
2902     command_func_t* func;
2903     char*arguments;
2904 } arguments[] =
2905 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
2906  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
2907  // "import" type stuff
2908  {"swf", c_swf, "name filename"},
2909  {"shape", c_swf, "name filename"},
2910  {"jpeg", c_image, "name filename quality=80%"},
2911  {"png", c_image, "name filename"},
2912  {"movie", c_movie, "name filename"},
2913  {"sound", c_sound, "name filename"},
2914  {"font", c_font, "name filename"},
2915  {"soundtrack", c_soundtrack, "filename"},
2916  {"quicktime", c_quicktime, "url"},
2917
2918     // generators of primitives
2919
2920  {"point", c_point, "name x=0 y=0"},
2921  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
2922  {"outline", c_outline, "name format=simple"},
2923  {"textshape", c_textshape, "name font size=100% text"},
2924
2925     // character generators
2926  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
2927  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
2928  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
2929
2930  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
2931  {"text", c_text, "name text font size=100% color=white"},
2932  {"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="},
2933  {"morphshape", c_morphshape, "name start end"},
2934  {"button", c_button, "name"},
2935     {"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="},
2936     {"on_press", c_on_press, "position=inside"},
2937     {"on_release", c_on_release, "position=anywhere"},
2938     {"on_move_in", c_on_move_in, "state=not_pressed"},
2939     {"on_move_out", c_on_move_out, "state=not_pressed"},
2940     {"on_key", c_on_key, "key=any"},
2941  
2942     // control tags
2943  {"play", c_play, "name loop=0 @nomultiple=0"},
2944  {"stop", c_stop, "name= "},
2945  {"nextframe", c_nextframe, "name"},
2946  {"previousframe", c_previousframe, "name"},
2947
2948     // object placement tags
2949  {"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="},
2950  {"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="},
2951  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2952  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2953  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2954  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= ratio= above= below="},
2955  {"del", c_del, "name"},
2956     // virtual object placement
2957  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
2958
2959     // commands which start a block
2960 //startclip (see above)
2961  {"sprite", c_sprite, "name"},
2962  {"action", c_action, "filename="},
2963  {"initaction", c_initaction, "name filename="},
2964
2965  {"end", c_end, ""}
2966 };
2967
2968
2969 static map_t parseArguments(char*command, char*pattern)
2970 {
2971     char*x;
2972     char*d,*e;
2973    
2974     string_t name[64];
2975     string_t value[64];
2976     int set[64];
2977     int isboolean[64];
2978     int pos;
2979     int len;
2980     int t;
2981     string_t t1,t2;
2982     map_t result;
2983     map_init(&result);
2984
2985     string_set(&t1, "commandname");
2986     string_set(&t2, command);
2987     map_put(&result, t1, t2);
2988
2989     if(!pattern || !*pattern)
2990         return result;
2991
2992     x = pattern;
2993
2994     pos = 0;
2995
2996     if(!strncmp("<i> ", x, 3)) {
2997         readToken();
2998         if(type == COMMAND || type == RAWDATA) {
2999             pushBack();
3000             syntaxerror("character name expected");
3001         }
3002         name[pos].str = "instance";
3003         name[pos].len = 8;
3004         value[pos].str = text;
3005         value[pos].len = strlen(text);
3006         set[pos] = 1;
3007         pos++;
3008
3009         if(type == ASSIGNMENT)
3010             readToken();
3011
3012         name[pos].str = "character";
3013         name[pos].len = 9;
3014         value[pos].str = text;
3015         value[pos].len = strlen(text);
3016         set[pos] = 1;
3017         pos++;
3018
3019         x+=4;
3020     }
3021
3022     while(*x) {
3023         isboolean[pos] = (x[0] =='@');
3024         if(isboolean[pos])
3025             x++;
3026
3027         d = strchr(x, ' ');
3028         e = strchr(x, '=');
3029         if(!d)
3030             d=&x[strlen(x)];
3031         set[pos] = 0;
3032
3033         if(!e || d<e) { 
3034             // no default
3035             name[pos].str = x;
3036             name[pos].len = d-x;
3037             value[pos].str = 0;
3038             value[pos].len = 0;
3039         } else {
3040             name[pos].str = x;
3041             name[pos].len = e-x;
3042             value[pos].str = e+1;
3043             value[pos].len = d-e-1;
3044         }
3045         pos++;
3046         if(!*d) break;
3047         x=d+1;
3048     }
3049     len = pos;
3050
3051 /*    for(t=0;t<len;t++) {
3052         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
3053                 isboolean[t]?"(boolean)":"");
3054     }*/
3055
3056     while(1) {
3057         readToken();
3058         if(type == RAWDATA || type == COMMAND) {
3059             pushBack();
3060             break;
3061         }
3062
3063         // first, search for boolean arguments
3064         for(pos=0;pos<len;pos++)
3065         {
3066             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
3067                 set[pos] = 1;
3068                 if(type == ASSIGNMENT)
3069                     readToken();
3070                 value[pos].str = text;
3071                 value[pos].len = strlen(text);
3072                 /*printf("setting boolean parameter %s (to %s)\n",
3073                         strdup_n(name[pos], namelen[pos]),
3074                         strdup_n(value[pos], valuelen[pos]));*/
3075                 break;
3076             }
3077         }
3078
3079         // second, search for normal arguments
3080         if(pos==len)
3081         for(pos=0;pos<len;pos++)
3082         {
3083             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
3084                (type != ASSIGNMENT && !set[pos])) {
3085                 if(set[pos]) {
3086                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
3087                 }
3088                 if(type == ASSIGNMENT)
3089                     readToken();
3090                 set[pos] = 1;
3091                 value[pos].str = text;
3092                 value[pos].len = strlen(text);
3093 #if 0//def DEBUG
3094                 printf("setting parameter %s (to %s)\n",
3095                         strdup_n(name[pos].str, name[pos].len),
3096                         strdup_n(value[pos].str, value[pos].len));
3097 #endif
3098                 break;
3099             }
3100         }
3101         if(pos==len) {
3102             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
3103         }
3104     }
3105 #if 0//def DEBUG
3106     for(t=0;t<len;t++) {
3107         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
3108     }
3109 #endif
3110     for(t=0;t<len;t++) {
3111         if(value[t].str && value[t].str[0] == '*') {
3112             //relative default- take value from some other parameter
3113             int s;
3114             for(s=0;s<len;s++) {
3115                 if(value[s].len == value[t].len-1 &&
3116                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
3117                     value[t].str = value[s].str;
3118             }
3119         }
3120         if(value[t].str == 0) {
3121             pushBack();
3122             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
3123         }
3124     }
3125
3126     /* ok, now construct the dictionary from the parameters */
3127
3128     for(t=0;t<len;t++) 
3129     {
3130         map_put(&result, name[t], value[t]);
3131     }
3132     return result;
3133 }
3134 static void parseArgumentsForCommand(char*command)
3135 {
3136     int t;
3137     map_t args;
3138     int nr = -1;
3139     msg("<verbose> parse Command: %s (line %d)", command, line);
3140
3141     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
3142         if(!strcmp(arguments[t].command, command)) {
3143
3144             /* ugly hack- will be removed soon (once documentation and .sc generating
3145                utilities have been changed) */
3146             if(!strcmp(command, "swf") && !stackpos) {
3147                 warning("Please use .flash instead of .swf- this will be mandatory soon");      
3148                 command = "flash";
3149                 t = 0;
3150             }
3151
3152             args = parseArguments(command, arguments[t].arguments);
3153             nr = t;
3154             break;
3155         }
3156     }
3157     if(nr<0)
3158         syntaxerror("command %s not known", command);
3159    
3160     // catch missing .flash directives at the beginning of a file
3161     if(strcmp(command, "flash") && !stackpos)
3162     {
3163         syntaxerror("No movie defined- use .flash first");
3164     }
3165
3166 #ifdef DEBUG
3167     printf(".%s\n", command);fflush(stdout);
3168     map_dump(&args, stdout, "\t");fflush(stdout);
3169 #endif
3170
3171     (*arguments[nr].func)(&args);
3172
3173     /*if(!strcmp(command, "button") ||
3174        !strcmp(command, "action")) {
3175         while(1) {
3176             readToken();
3177             if(type == COMMAND) {
3178                 if(!strcmp(text, "end"))
3179                     break;
3180                 else {
3181                     pushBack();
3182                     break;
3183                 }
3184             }
3185         }
3186     }*/
3187
3188     map_clear(&args);
3189     return;
3190 }
3191
3192 int main (int argc,char ** argv)
3193
3194     int t;
3195     processargs(argc, argv);
3196     initLog(0,-1,0,0,-1,verbose);
3197
3198     if(!filename) {
3199         args_callback_usage(argv[0]);
3200         exit(1);
3201     }
3202     
3203     file = generateTokens(filename);
3204     if(!file) {
3205         fprintf(stderr, "parser returned error.\n");
3206         return 1;
3207     }
3208     pos=0;
3209     t=0;
3210
3211     while(!noMoreTokens()) {
3212         readToken();
3213         if(type != COMMAND)
3214             syntaxerror("command expected");
3215         parseArgumentsForCommand(text);
3216     }
3217
3218     s_close();
3219     freeTokens(file);
3220
3221     return 0;
3222 }
3223