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