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