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