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