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