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