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