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