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