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