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