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