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