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