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