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