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