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