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