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