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