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