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