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