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