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