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