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