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