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