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