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