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