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