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