renamed png functions
[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     unsigned 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         png_load(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_createfont(const char*name, const char*filename, const char*glyphs, char flashtype)
1593 {
1594     if(dict_lookup(&fonts, name))
1595         syntaxerror("font %s defined twice", name);
1596
1597     SWFFONT* font = swf_LoadFont(filename, flashtype);
1598     if(font == 0) {
1599         warning("Couldn't open font file \"%s\"", filename);
1600         font = (SWFFONT*)malloc(sizeof(SWFFONT));
1601         memset(font, 0, sizeof(SWFFONT));
1602         dict_put(&fonts, name, font);
1603         return;
1604     }
1605     swf_FontPrepareForEditText(font);
1606
1607     if(!strcmp(glyphs, "all")) {
1608         swf_FontUseAll(font);
1609         font->use->glyphs_specified = 1;
1610     } else {
1611         if(!glyphs[0]) {
1612             swf_FontInitUsage(font);
1613         } else {
1614             swf_FontUseUTF8(font, (const U8*)glyphs, 0xffff);
1615             font->use->glyphs_specified = 1;
1616         }
1617     }
1618     dict_put(&fonts, name, font);
1619 }
1620
1621 void s_font(const char*name, const char*filename)
1622 {
1623     SWFFONT* font;
1624     font = dict_lookup(&fonts, name);
1625     font->id = id;
1626     swf_FontReduce_swfc(font);
1627         
1628     if(font->version>=3 && stack[0].swf->fileVersion < 8) {
1629         warning("flashtype not supported for flash versions 8 and below");
1630     }
1631
1632     tag = swf_InsertTag(tag, font->version==3?ST_DEFINEFONT3:ST_DEFINEFONT2);
1633     swf_FontSetDefine2(tag, font);
1634
1635     if(do_exports) {
1636         tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1637         swf_SetU16(tag, 1);
1638         swf_SetU16(tag, id);
1639         swf_SetString(tag, name);
1640     }
1641
1642     incrementid();
1643 }
1644
1645
1646
1647 typedef struct _sound_t
1648 {
1649     U16 id;
1650     TAG*tag;
1651 } sound_t;
1652
1653 void s_sound(const char*name, const char*filename)
1654 {
1655     struct WAV wav, wav2;
1656     struct MP3 mp3;
1657     sound_t* sound;
1658     U16*samples = NULL;
1659     unsigned numsamples = 1;
1660     unsigned blocksize = 1152;
1661     int is_mp3 = 0;
1662
1663     if(dict_lookup(&sounds, name))
1664         syntaxerror("sound %s defined twice", name);
1665
1666     if(wav_read(&wav, filename))
1667     {
1668         int t;
1669         wav_convert2mono(&wav, &wav2, 44100);
1670         samples = (U16*)wav2.data;
1671         numsamples = wav2.size/2;
1672         free(wav.data);
1673 #ifdef WORDS_BIGENDIAN
1674         /* swap bytes */
1675         for(t=0;t<numsamples;t++)
1676             samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1677 #endif
1678     }
1679     else
1680         if(mp3_read(&mp3, filename))
1681         {
1682             fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1683             blocksize = 1;
1684             is_mp3 = 1;
1685         }
1686         else
1687         {
1688             warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1689             samples = 0;
1690             numsamples = 0;
1691         }
1692
1693     if(numsamples%blocksize != 0)
1694     {
1695         // apply padding, so that block is a multiple of blocksize
1696         int numblocks = (numsamples+blocksize-1)/blocksize;
1697         int numsamples2;
1698         U16* samples2;
1699         numsamples2 = numblocks * blocksize;
1700         samples2 = malloc(sizeof(U16)*numsamples2);
1701         memcpy(samples2, samples, numsamples*sizeof(U16));
1702         memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1703         numsamples = numsamples2;
1704         free(samples);
1705         samples = samples2;
1706     }
1707
1708     tag = swf_InsertTag(tag, ST_DEFINESOUND);
1709     swf_SetU16(tag, id); //id
1710     if(is_mp3)
1711     {
1712         swf_SetSoundDefineMP3(
1713                 tag, mp3.data, mp3.size,
1714                 mp3.SampRate,
1715                 mp3.Channels,
1716                 mp3.NumFrames);
1717         mp3_clear(&mp3);
1718     }
1719     else
1720         swf_SetSoundDefine(tag, samples, numsamples);
1721
1722     if(do_exports) {
1723         tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1724         swf_SetU16(tag, id);
1725         swf_SetString(tag, name);
1726         tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1727         swf_SetU16(tag, 1);
1728         swf_SetU16(tag, id);
1729         swf_SetString(tag, name);
1730     }
1731
1732     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1733     sound->tag = tag;
1734     sound->id = id;
1735
1736     dict_put(&sounds, name, sound);
1737
1738     incrementid();
1739
1740     if(samples)
1741         free(samples);
1742 }
1743
1744 static char* gradient_getToken(const char**p)
1745 {
1746     const char*start;
1747     char*result;
1748     while(**p && strchr(" \t\n\r", **p)) {
1749         (*p)++;
1750     }
1751     start = *p;
1752     while(**p && !strchr(" \t\n\r", **p)) {
1753         (*p)++;
1754     }
1755     result = malloc((*p)-start+1);
1756     memcpy(result,start,(*p)-start+1);
1757     result[(*p)-start] = 0;
1758     return result;
1759 }
1760
1761 float parsePercent(const char*str);
1762 RGBA parseColor(const char*str);
1763
1764 GRADIENT parseGradient(const char*str)
1765 {
1766     GRADIENT gradient;
1767     int lastpos = -1;
1768     const char* p = str;
1769     memset(&gradient, 0, sizeof(GRADIENT));
1770     gradient.ratios = rfx_calloc(16*sizeof(U8));
1771     gradient.rgba = rfx_calloc(16*sizeof(RGBA));
1772
1773     while(*p)
1774     {
1775         char*posstr,*colorstr;
1776         int pos;
1777         RGBA color;
1778         posstr = gradient_getToken(&p);
1779         if(!*posstr)
1780         {
1781             free(posstr);
1782             break;
1783         }
1784         pos = (int)(parsePercent(posstr)*255.0);
1785         if(pos == lastpos)
1786             pos++;
1787         if(!*p)
1788         {
1789             rfx_free(gradient.ratios);
1790             rfx_free(gradient.rgba);
1791             free(posstr);
1792             syntaxerror("Error in shape data: Color expected after %s", posstr);
1793         }
1794         colorstr = gradient_getToken(&p);
1795         color = parseColor(colorstr);
1796         if(gradient.num == 16)
1797         {
1798             warning("gradient record too big- max size is 16, rest ignored");
1799             break;
1800         }
1801         gradient.ratios[gradient.num] = pos;
1802         gradient.rgba[gradient.num] = color;
1803         gradient.num++;
1804         free(posstr);
1805         free(colorstr);
1806         lastpos = pos;
1807         }
1808     return gradient;
1809 }
1810
1811 FILTERLIST* parseFilters(char* list)
1812 {
1813     if(!strcmp(list, "no_filters"))
1814         return 0;
1815     FILTER* f;
1816     FILTERLIST* f_list = (FILTERLIST*)malloc(sizeof(FILTERLIST));
1817     f_list->num = 0;
1818     char* f_start = list;
1819     char* f_end;
1820     while (f_start)
1821     {
1822         f_end = strchr(f_start, ',');
1823         if(f_end)
1824             *f_end = '\0';
1825         f = dict_lookup(&filters, f_start);
1826         if(!f)
1827         {
1828             free(f_list);
1829             syntaxerror("unknown filter %s", f_start);
1830         }
1831         if(f_list->num == 8)
1832         {
1833             warning("too many filters in filterlist, no more than 8 please, rest ignored");
1834             break;
1835         }
1836         f_list->filter[f_list->num] = f;
1837         f_list->num++;
1838         if(f_end)
1839         {
1840             *f_end = ',';
1841             f_start = f_end + 1;
1842         }
1843         else
1844             f_start = 0;
1845     }
1846     return f_list;
1847 }
1848
1849 void s_gradient(const char*name, const char*text, int radial, int rotate)
1850 {
1851     gradient_t* gradient;
1852     gradient = malloc(sizeof(gradient_t));
1853     memset(gradient, 0, sizeof(gradient_t));
1854     gradient->gradient = parseGradient(text);
1855     gradient->radial = radial;
1856     gradient->rotate = rotate;
1857
1858     dict_put(&gradients, name, gradient);
1859 }
1860
1861 void s_gradientglow(const char*name, const char*gradient, float blurx, float blury,
1862                     float angle, float distance, float strength, char innershadow,
1863                     char knockout, char composite, char ontop, int passes)
1864 {
1865     if(dict_lookup(&filters, name))
1866         syntaxerror("filter %s defined twice", name);
1867
1868     gradient_t* g = dict_lookup(&gradients, gradient);
1869     if(!g)
1870         syntaxerror("unknown gradient %s", gradient);
1871
1872     composite = 1;
1873
1874     FILTER_GRADIENTGLOW* filter = rfx_calloc(sizeof(FILTER_GRADIENTGLOW));
1875     filter->type = FILTERTYPE_GRADIENTGLOW;
1876     filter->gradient = &g->gradient;
1877     filter->blurx = blurx;
1878     filter->blury = blury;
1879     filter->strength = strength;
1880     filter->angle = angle;
1881     filter->distance = distance;
1882     filter->innershadow = innershadow;
1883     filter->knockout = knockout;
1884     filter->composite = composite;
1885     filter->ontop = ontop;
1886     filter->passes = passes;
1887
1888     dict_put(&filters, name, filter);
1889 }
1890
1891 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)
1892 {
1893     if(dict_lookup(&filters, name))
1894         syntaxerror("filter %s defined twice", name);
1895
1896     composite = 1;
1897     FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
1898     filter->type = FILTERTYPE_DROPSHADOW;
1899     filter->color= color;
1900     filter->blurx = blurx;
1901     filter->blury = blury;
1902     filter->strength = strength;
1903     filter->angle = angle;
1904     filter->distance = distance;
1905     filter->innershadow = innershadow;
1906     filter->knockout = knockout;
1907     filter->composite = composite;
1908     filter->passes = passes;
1909
1910     dict_put(&filters, name, filter);
1911 }
1912
1913 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)
1914 {
1915     if(dict_lookup(&filters, name))
1916         syntaxerror("filter %s defined twice", name);
1917
1918     composite = 1;
1919     FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
1920     filter->type = FILTERTYPE_BEVEL;
1921     filter->shadow = shadow;
1922     filter->highlight = highlight;
1923     filter->blurx = blurx;
1924     filter->blury = blury;
1925     filter->strength = strength;
1926     filter->angle = angle;
1927     filter->distance = distance;
1928     filter->innershadow = innershadow;
1929     filter->knockout = knockout;
1930     filter->composite = composite;
1931     filter->ontop = ontop;
1932     filter->passes = passes;
1933
1934     dict_put(&filters, name, filter);
1935 }
1936
1937 void s_blur(const char*name, double blurx, double blury, int passes)
1938 {
1939     if(dict_lookup(&filters, name))
1940         syntaxerror("filter %s defined twice", name);
1941
1942     FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
1943     filter->type = FILTERTYPE_BLUR;
1944     filter->blurx = blurx;
1945     filter->blury = blury;
1946     filter->passes = passes;
1947
1948     dict_put(&filters, name, filter);
1949 }
1950
1951 void s_action(const char*text)
1952 {
1953     if(stack[0].swf->fileVersion < 9) {
1954         ActionTAG* a = 0;
1955         a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1956         if(!a) {
1957             swf_ActionFree(a);
1958             syntaxerror("Couldn't compile ActionScript");
1959         }
1960         tag = swf_InsertTag(tag, ST_DOACTION);
1961         swf_ActionSet(tag, a);
1962         swf_ActionFree(a);
1963     } else {
1964         as3_parse_bytearray(stack[0].filename, text, strlen(text));
1965         stack[0].as3 = 1;
1966     }
1967 }
1968
1969 void s_initaction(const char*character, const char*text)
1970 {
1971     ActionTAG* a = 0;
1972     character_t*c = 0;
1973     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1974     if(!a)
1975     {
1976         swf_ActionFree(a);
1977         syntaxerror("Couldn't compile ActionScript");
1978     }
1979
1980     c = (character_t*)dict_lookup(&characters, character);
1981
1982     tag = swf_InsertTag(tag, ST_DOINITACTION);
1983     swf_SetU16(tag, c->id);
1984     swf_ActionSet(tag, a);
1985
1986     swf_ActionFree(a);
1987 }
1988
1989 int s_swf3action(const char*name, const char*action)
1990 {
1991     ActionTAG* a = 0;
1992     instance_t* object = 0;
1993     if(name)
1994         object = (instance_t*)dict_lookup(&instances, name);
1995     if(!object && name && *name) {
1996         /* we have a name, but couldn't find it. Abort. */
1997         return 0;
1998     }
1999     a = action_SetTarget(0, name);
2000     if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
2001     else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
2002     else if(!strcmp(action, "stop")) a = action_Stop(a);
2003     else if(!strcmp(action, "play")) a = action_Play(a);
2004     a = action_SetTarget(a, "");
2005     a = action_End(a);
2006
2007     tag = swf_InsertTag(tag, ST_DOACTION);
2008     swf_ActionSet(tag, a);
2009     swf_ActionFree(a);
2010     return 1;
2011 }
2012
2013 void s_outline(const char*name, const char*format, const char*source)
2014 {
2015     if(dict_lookup(&outlines, name))
2016         syntaxerror("outline %s defined twice", name);
2017
2018     outline_t* outline;
2019
2020     drawer_t draw;
2021     SHAPE* shape;
2022     SHAPE2* shape2;
2023     SRECT bounds;
2024
2025     //swf_Shape10DrawerInit(&draw, 0);
2026     swf_Shape11DrawerInit(&draw, 0);
2027
2028     draw_string(&draw, source);
2029     draw.finish(&draw);
2030     shape = swf_ShapeDrawerToShape(&draw);
2031     bounds = swf_ShapeDrawerGetBBox(&draw);
2032     draw.dealloc(&draw);
2033
2034     outline = (outline_t*)rfx_calloc(sizeof(outline_t));
2035     outline->shape = shape;
2036     outline->bbox = bounds;
2037
2038     dict_put(&outlines, name, outline);
2039 }
2040
2041 int s_playsound(const char*name, int loops, int nomultiple, int stop)
2042 {
2043     sound_t* sound;
2044     SOUNDINFO info;
2045     if(!name)
2046         return 0;
2047     sound = dict_lookup(&sounds, name);
2048     if(!sound)
2049         return 0;
2050
2051     tag = swf_InsertTag(tag, ST_STARTSOUND);
2052     swf_SetU16(tag, sound->id); //id
2053     memset(&info, 0, sizeof(info));
2054     info.stop = stop;
2055     info.loops = loops;
2056     info.nomultiple = nomultiple;
2057     swf_SetSoundInfo(tag, &info);
2058     return 1;
2059 }
2060
2061 void s_includeswf(const char*name, const char*filename)
2062 {
2063     int f;
2064     SWF swf;
2065     TAG* ftag;
2066     SRECT r;
2067     TAG* s;
2068     int level = 0;
2069     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
2070     f = open(filename,O_RDONLY|O_BINARY);
2071     if(f<0) {
2072         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
2073         s_box(name, 0, 0, black, 20, 0);
2074         return;
2075     }
2076     if(swf_ReadSWF(f,&swf)<0) {
2077         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
2078         s_box(name, 0, 0, black, 20, 0);
2079         return;
2080     }
2081     close(f);
2082
2083     /* FIXME: The following sets the bounding Box for the character.
2084               It is wrong for two reasons:
2085               a) It may be too small (in case objects in the movie clip at the borders)
2086               b) it may be too big (because the poor movie never got autocropped)
2087     */
2088     r = swf.movieSize;
2089
2090     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
2091     swf_SetU16(tag, id);
2092     swf_SetU16(tag, swf.frameCount);
2093
2094     swf_Relocate(&swf, idmap);
2095
2096     ftag = swf.firstTag;
2097     level = 1;
2098     while(ftag) {
2099         int t;
2100         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
2101             if(cutout[t] == ftag->id) {
2102                 ftag = ftag->next;
2103                 continue;
2104             }
2105         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
2106             level++;
2107         if(ftag->id == ST_END)
2108             level--;
2109         if(!level)
2110             break;
2111
2112         if(ftag->id != ST_SETBACKGROUNDCOLOR) {
2113             /* We simply dump all tags right after the sprite
2114                header, relying on the fact that swf_OptimizeTagOrder() will
2115                sort things out for us later.
2116                We also rely on the fact that the imported SWF is well-formed.
2117              */
2118             tag = swf_InsertTag(tag, ftag->id);
2119             swf_SetBlock(tag, ftag->data, ftag->len);
2120         }
2121
2122         ftag = ftag->next;
2123     }
2124     if(!ftag)
2125         syntaxerror("Included file %s contains errors", filename);
2126     tag = swf_InsertTag(tag, ST_END);
2127
2128     swf_FreeTags(&swf);
2129
2130     s_addcharacter(name, id, tag, r);
2131     incrementid();
2132 }
2133 SRECT s_getCharBBox(const char*name)
2134 {
2135     character_t* c = dict_lookup(&characters, name);
2136     if(!c) syntaxerror("character '%s' unknown(2)", name);
2137     return c->size;
2138 }
2139 SRECT s_getInstanceBBox(const char*name)
2140 {
2141     instance_t * i = dict_lookup(&instances, name);
2142     character_t * c;
2143     if(!i) syntaxerror("instance '%s' unknown(4)", name);
2144     c = i->character;
2145     if(!c) syntaxerror("internal error(5)");
2146     return c->size;
2147 }
2148 void s_getParameters(const char*name, parameters_t* p)
2149 {
2150     instance_t * i = dict_lookup(&instances, name);
2151     if(!i)
2152         syntaxerror("instance '%s' unknown(10)", name);
2153     if(change_sets_all)
2154         readParameters(i->history, p, currentframe);
2155     else
2156         *p = i->parameters;
2157 }
2158
2159 void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
2160 {
2161     history_begin(i->history, "x", currentframe, tag, p->x);
2162     history_begin(i->history, "y", currentframe, tag, p->y);
2163     history_begin(i->history, "scalex", currentframe, tag, p->scalex);
2164     history_begin(i->history, "scaley", currentframe, tag, p->scaley);
2165     history_begin(i->history, "cxform.r0", currentframe, tag, p->cxform.r0);
2166     history_begin(i->history, "cxform.g0", currentframe, tag, p->cxform.g0);
2167     history_begin(i->history, "cxform.b0", currentframe, tag, p->cxform.b0);
2168     history_begin(i->history, "cxform.a0", currentframe, tag, p->cxform.a0);
2169     history_begin(i->history, "cxform.r1", currentframe, tag, p->cxform.r1);
2170     history_begin(i->history, "cxform.g1", currentframe, tag, p->cxform.g1);
2171     history_begin(i->history, "cxform.b1", currentframe, tag, p->cxform.b1);
2172     history_begin(i->history, "cxform.a1", currentframe, tag, p->cxform.a1);
2173     history_begin(i->history, "rotate", currentframe, tag, p->rotate);
2174     history_begin(i->history, "shear", currentframe, tag, p->shear);
2175     history_begin(i->history, "pivot.x", currentframe, tag, p->pivot.x);
2176     history_begin(i->history, "pivot.y", currentframe, tag, p->pivot.y);
2177     history_begin(i->history, "pin.x", currentframe, tag, p->pin.x);
2178     history_begin(i->history, "pin.y", currentframe, tag, p->pin.y);
2179     history_begin(i->history, "blendmode", currentframe, tag, p->blendmode);
2180     history_beginFilter(i->history, currentframe, tag, p->filters);
2181     history_begin(i->history, "flags", currentframe, tag, 0);
2182 }
2183
2184 void s_startclip(const char*instance, const char*character, parameters_t p)
2185 {
2186     character_t* c = dict_lookup(&characters, character);
2187     instance_t* i;
2188     MATRIX m;
2189     if(!c) {
2190         syntaxerror("character %s not known", character);
2191     }
2192     i = s_addinstance(instance, c, currentdepth);
2193     i->parameters = p;
2194     m = s_instancepos(i->character->size, &p);
2195
2196     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2197     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
2198     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
2199
2200     stack[stackpos].tag = tag;
2201     stack[stackpos].type = 2;
2202     stackpos++;
2203
2204     setStartparameters(i, &p, tag);
2205     currentdepth++;
2206 }
2207 void s_endClip()
2208 {
2209     SWFPLACEOBJECT p;
2210     stackpos--;
2211     swf_SetTagPos(stack[stackpos].tag, 0);
2212     swf_GetPlaceObject(stack[stackpos].tag, &p);
2213     p.clipdepth = currentdepth;
2214     //p.name = 0;
2215     swf_ClearTag(stack[stackpos].tag);
2216     swf_SetPlaceObject(stack[stackpos].tag, &p);
2217     currentdepth++;
2218 }
2219
2220 void s_put(const char*instance, const char*character, parameters_t p)
2221 {
2222     character_t* c = dict_lookup(&characters, character);
2223     instance_t* i;
2224     MATRIX m;
2225     if(!c)
2226         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
2227
2228     i = s_addinstance(instance, c, currentdepth);
2229     i->parameters = p;
2230     m = s_instancepos(i->character->size, &p);
2231
2232     if(p.blendmode || p.filters)
2233     {
2234         if(stack[0].swf->fileVersion < 8)
2235         {
2236             if(p.blendmode)
2237                 warning("blendmodes only supported for flash version>=8");
2238             else
2239                 warning("filters only supported for flash version>=8");
2240         }
2241         tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
2242         }
2243     else
2244         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2245     setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
2246     setStartparameters(i, &p, tag);
2247     currentdepth++;
2248 }
2249
2250 void recordChanges(history_t* history, parameters_t p, int changeFunction, interpolation_t* inter)
2251 {
2252     if(p.set & SF_X)
2253         history_remember(history, "x", currentframe, changeFunction, p.x, inter);
2254     if(p.set & SF_Y)
2255         history_remember(history, "y", currentframe, changeFunction, p.y, inter);
2256     if(p.set & SF_SCALEX)
2257         history_remember(history, "scalex", currentframe, changeFunction, p.scalex, inter);
2258     if(p.set & SF_SCALEY)
2259         history_remember(history, "scaley", currentframe, changeFunction, p.scaley, inter);
2260     if(p.set & SF_CX_R)
2261     {
2262         history_remember(history, "cxform.r0", currentframe, changeFunction, p.cxform.r0, inter);
2263         history_remember(history, "cxform.r1", currentframe, changeFunction, p.cxform.r1, inter);
2264     }
2265     if(p.set & SF_CX_G)
2266     {
2267         history_remember(history, "cxform.g0", currentframe, changeFunction, p.cxform.g0, inter);
2268         history_remember(history, "cxform.g1", currentframe, changeFunction, p.cxform.g1, inter);
2269     }
2270     if(p.set & SF_CX_B)
2271     {
2272         history_remember(history, "cxform.b0", currentframe, changeFunction, p.cxform.b0, inter);
2273         history_remember(history, "cxform.b1", currentframe, changeFunction, p.cxform.b1, inter);
2274     }
2275     if(p.set & SF_CX_A)
2276     {
2277         history_remember(history, "cxform.a0", currentframe, changeFunction, p.cxform.a0, inter);
2278         history_remember(history, "cxform.a1", currentframe, changeFunction, p.cxform.a1, inter);
2279     }
2280     if(p.set & SF_ROTATE)
2281         history_remember(history, "rotate", currentframe, changeFunction, p.rotate, inter);
2282     if(p.set & SF_SHEAR)
2283         history_remember(history, "shear", currentframe, changeFunction, p.shear, inter);
2284     if(p.set & SF_PIVOT)
2285     {
2286         history_remember(history, "pivot.x", currentframe, changeFunction, p.pivot.x, inter);
2287         history_remember(history, "pivot.y", currentframe, changeFunction, p.pivot.y, inter);
2288     }
2289     if(p.set & SF_PIN)
2290     {
2291         history_remember(history, "pin.x", currentframe, changeFunction, p.pin.x, inter);
2292         history_remember(history, "pin.y", currentframe, changeFunction, p.pin.y, inter);
2293     }
2294     if(p.set & SF_BLEND)
2295         history_remember(history, "blendmode", currentframe, changeFunction, p.blendmode, inter);
2296     if(p.set & SF_FILTER)
2297         history_rememberFilter(history, currentframe, changeFunction, p.filters, inter);
2298 }
2299
2300 void s_jump(const char* instance, parameters_t p)
2301 {
2302     instance_t* i = dict_lookup(&instances, instance);
2303     if(!i)
2304         syntaxerror("instance %s not known", instance);
2305     recordChanges(i->history, p, CF_JUMP, 0);
2306 }
2307
2308 void s_change(const char*instance, parameters_t p, interpolation_t* inter)
2309 {
2310     instance_t* i = dict_lookup(&instances, instance);
2311     if(!i)
2312         syntaxerror("instance %s not known", instance);
2313     recordChanges(i->history, p, CF_CHANGE, inter);
2314 }
2315
2316 void s_sweep(const char* instance, parameters_t p, float radius, int clockwise, int short_arc, interpolation_t* inter)
2317 {
2318     instance_t* i = dict_lookup(&instances, instance);
2319     if(!i)
2320         syntaxerror("instance %s not known", instance);
2321     history_rememberSweep(i->history, currentframe, p.x, p.y, radius, clockwise, short_arc, inter);
2322 }
2323
2324 void s_toggle(const char* instance, U16 flagsOn, U16 flagsOff)
2325 {
2326     instance_t* i = dict_lookup(&instances, instance);
2327     if(!i)
2328         syntaxerror("instance %s not known", instance);
2329     U16 flags = (U16)history_value(i->history, currentframe, "flags");
2330     flags |= flagsOn;
2331     flags &= flagsOff;
2332     history_remember(i->history, "flags", currentframe, CF_JUMP, flags, 0);
2333 }
2334
2335 void s_delinstance(const char*instance)
2336 {
2337     instance_t* i = dict_lookup(&instances, instance);
2338     if(!i)
2339         syntaxerror("instance %s not known", instance);
2340     writeInstance(i);
2341     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
2342     swf_SetU16(tag, i->depth);
2343     dict_del(&instances, instance);
2344     free(i);
2345 }
2346
2347 void s_schange(const char*instance, parameters_t p, interpolation_t* inter)
2348 {
2349     instance_t* i = dict_lookup(&instances, instance);
2350     if(!i)
2351         syntaxerror("instance %s not known", instance);
2352     recordChanges(i->history, p, CF_SCHANGE, inter);
2353 }
2354
2355 void s_end()
2356 {
2357     if(!stackpos)
2358         syntaxerror(".end unexpected");
2359     switch (stack[stackpos-1].type)
2360     {
2361         case 0:
2362             s_endSWF();
2363             break;
2364         case 1:
2365             s_endSprite();
2366             break;
2367         case 2:
2368             s_endClip();
2369             break;
2370         case 3:
2371             s_endButton();
2372             break;
2373         default:
2374             syntaxerror("internal error 1");
2375     }
2376 }
2377
2378 // ------------------------------------------------------------------------
2379
2380 typedef int command_func_t(map_t*args);
2381
2382 SRECT parseBox(const char*str)
2383 {
2384     SRECT r = {0,0,0,0};
2385     float xmin, xmax, ymin, ymax;
2386     char*x = strchr(str, 'x');
2387     char*d1=0,*d2=0;
2388     if(!strcmp(str, "autocrop")) {
2389         r.xmin = r.ymin = r.xmax = r.ymax = 0;
2390         return r;
2391     }
2392     if(!x) goto error;
2393     d1 = strchr(x+1, ':');
2394     if(d1)
2395         d2 = strchr(d1+1, ':');
2396     if(!d1) {
2397         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
2398             goto error;
2399         xmin = ymin = 0;
2400     }
2401     else if(d1 && !d2) {
2402         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
2403             goto error;
2404         xmax += xmin;
2405         ymin = 0;
2406     }
2407     else {
2408         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
2409             goto error;
2410         xmax += xmin;
2411         ymax += ymin;
2412     }
2413     r.xmin = (SCOORD)(xmin*20);
2414     r.ymin = (SCOORD)(ymin*20);
2415     r.xmax = (SCOORD)(xmax*20);
2416     r.ymax = (SCOORD)(ymax*20);
2417     return r;
2418 error:
2419     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
2420     return r;
2421 }
2422 float parseFloat(const char*str)
2423 {
2424     return atof(str);
2425 }
2426 int parseInt(const char*str)
2427 {
2428     int t;
2429     int l=strlen(str);
2430     int s=0;
2431     if(str[0]=='+' || str[0]=='-')
2432         s++;
2433
2434     for(t=s;t<l;t++)
2435         if(str[t]<'0' || str[t]>'9')
2436             syntaxerror("Not an Integer: \"%s\"", str);
2437     return atoi(str);
2438 }
2439 static double parseRawTwip(const char*str)
2440 {
2441     char*dot;
2442     int sign=1;
2443     if(str[0]=='+' || str[0]=='-') {
2444         if(str[0]=='-')
2445             sign = -1;
2446         str++;
2447     }
2448     dot = strchr(str, '.');
2449     if(!dot) {
2450         int l=strlen(str);
2451         int t;
2452         return sign*parseInt(str);
2453     } else {
2454         char* old = strdup(str);
2455         int l=strlen(dot+1);
2456         const char*s;
2457         *dot++ = 0;
2458         for(s=str;s<dot-1;s++) {
2459             if(*s<'0' || *s>'9')
2460             {
2461                 free(old);
2462                 syntaxerror("Not a coordinate: \"%s\"", str);
2463             }
2464         }
2465         for(s=dot;*s;s++) {
2466             if(*s<'0' || *s>'9')
2467             {
2468                 free(old);
2469                 syntaxerror("Not a coordinate: \"%s\"", str);
2470             }
2471         }
2472         if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
2473             dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
2474             dot[2] = 0;
2475             l=2;
2476             warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
2477         }
2478         free(old);
2479         if(l==0)
2480             return sign*(atoi(str));
2481         if(l==1)
2482             return sign*(atoi(str)+0.1*atoi(dot));
2483         if(l==2)
2484             return sign*(atoi(str)+0.01*atoi(dot));
2485     }
2486     return 0;
2487 }
2488
2489 static dict_t defines;
2490 static int defines_initialized = 0;
2491 static mem_t define_values;
2492
2493 static double parseNameOrTwip(const char*s)
2494 {
2495     int l = 0;
2496     double v;
2497     if(defines_initialized) {
2498         l = (int)dict_lookup(&defines, s);
2499     }
2500     if(l) {
2501         return *(int*)&define_values.buffer[l-1];
2502     } else {
2503         return parseRawTwip(s);
2504     }
2505 }
2506
2507 /* automatically generated by yiyiyacc, http://www.quiss.org/yiyiyacc/ */
2508 static double parseExpression(char*s)
2509 {
2510     int chr2index[256];
2511     memset(chr2index, -1, sizeof(chr2index));
2512     chr2index['+'] = 0;
2513     chr2index['-'] = 1;
2514     chr2index['*'] = 2;
2515     chr2index['/'] = 3;
2516     chr2index['('] = 5;
2517     chr2index[')'] = 6;
2518     chr2index['\0'] = 7;
2519
2520     int stackpos = 1;
2521     int stack[256];
2522     double values[256];
2523     stack[0]=0;
2524     values[0]=0;
2525     int accept = 18;
2526     int left[10]={11,8,8,8,8,9,9,9,10,10}; //production left side
2527     int plen[10]={1,3,2,3,1,3,3,1,1,3}; //production size
2528     int table[18][12] = {
2529         {0, 4, 0, 0, 5, 6, 0, 0, 1, 2, 3, 0},
2530         {7, 8, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0},
2531         {-4, -4, 9, 10, 0, 0, -4, -4, 0, 0, 0, 0},
2532         {-7, -7, -7, -7, 0, 0, -7, -7, 0, 0, 0, 0},
2533         {0, 0, 0, 0, 5, 6, 0, 0, 0, 11, 3, 0},
2534         {-8, -8, -8, -8, 0, 0, -8, -8, 0, 0, 0, 0},
2535         {0, 4, 0, 0, 5, 6, 0, 0, 12, 2, 3, 0},
2536         {0, 0, 0, 0, 5, 6, 0, 0, 0, 13, 3, 0},
2537         {0, 0, 0, 0, 5, 6, 0, 0, 0, 14, 3, 0},
2538         {0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 15, 0},
2539         {0, 0, 0, 0, 5, 6, 0, 0, 0, 0, 16, 0},
2540         {-2, -2, 9, 10, 0, 0, -2, -2, 0, 0, 0, 0},
2541         {7, 8, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0},
2542         {-1, -1, 9, 10, 0, 0, -1, -1, 0, 0, 0, 0},
2543         {-3, -3, 9, 10, 0, 0, -3, -3, 0, 0, 0, 0},
2544         {-5, -5, -5, -5, 0, 0, -5, -5, 0, 0, 0, 0},
2545         {-6, -6, -6, -6, 0, 0, -6, -6, 0, 0, 0, 0},
2546         {-9, -9, -9, -9, 0, 0, -9, -9, 0, 0, 0, 0}};
2547
2548     char*p = s;
2549     while(1) {
2550         char*pnext = p+1;
2551         int action;
2552         double value = 0;
2553         if(!stackpos) {
2554             fprintf(stderr, "Error in expression\n");
2555             return 0.0;
2556         }
2557
2558         if(chr2index[*p]<0) {
2559             action = table[stack[stackpos-1]][4];
2560             if(action>0) {
2561                 while(chr2index[*pnext]<0) 
2562                     pnext++;
2563                 char save = *pnext;
2564                 *pnext = 0;
2565                 value = parseNameOrTwip(p);
2566                 *pnext = save;
2567             }
2568         } else {
2569             action = table[stack[stackpos-1]][chr2index[*p]];
2570         }
2571
2572         if(action == accept) {
2573             return values[stack[stackpos-1]];
2574         } else if(action>0) { // shift
2575             if(stackpos>254) {
2576                 fprintf(stderr, "Stack overflow while parsing expression\n");
2577                 return 0.0;
2578             }
2579             values[stackpos]=value;
2580             stack[stackpos++]=action;
2581             p=pnext;
2582         } else if(action<0) { // reduce
2583             stackpos-=plen[-action];
2584             stack[stackpos] = table[stack[stackpos-1]][left[-action]];
2585             switch(-action) {
2586               case 1:
2587                 values[stackpos] = values[stackpos+0] + values[stackpos+2];
2588               break;
2589               case 2:
2590                 values[stackpos] = 0 - values[stackpos+1];
2591               break;
2592               case 3:
2593                 values[stackpos] = values[stackpos+0] - values[stackpos+2];
2594               break;
2595               case 5:
2596                 values[stackpos] = values[stackpos+0] * values[stackpos+2];
2597               break;
2598               case 6:
2599                 values[stackpos] = values[stackpos+0] / values[stackpos+2];
2600               break;
2601               case 9:
2602                 values[stackpos] = values[stackpos+1];
2603               break;
2604             }
2605             stackpos++;
2606         } else {
2607             fprintf(stderr, "Syntax error in expression\n");
2608             return 0.0;
2609         }
2610     }
2611 }
2612
2613 int parseTwip(const char*str)
2614 {
2615     char*str2 = (char*)str;
2616     int v = (int)(parseExpression(str2)*20);
2617     return v;
2618 }
2619
2620 int parseArc(const char* str)
2621 {
2622     if(!strcmp(str, "short"))
2623         return 1;
2624     if(!strcmp(str, "long"))
2625         return 0;
2626     syntaxerror("invalid value for the arc parameter: %s", str);
2627     return 1;
2628 }
2629
2630 int parseDir(const char* str)
2631 {
2632     if(!strcmp(str, "clockwise"))
2633         return 1;
2634     if(!strcmp(str, "counterclockwise"))
2635         return 0;
2636     syntaxerror("invalid value for the dir parameter: %s", str);
2637     return 1;
2638 }
2639
2640 int isPoint(const char*str)
2641 {
2642     if(strchr(str, '('))
2643         return 1;
2644     else
2645         return 0;
2646 }
2647
2648 SPOINT parsePoint(const char*str)
2649 {
2650     SPOINT p;
2651     char tmp[80];
2652     int l = strlen(str);
2653     char*comma = strchr(str, ',');
2654     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
2655         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
2656     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
2657     p.x = parseTwip(tmp);
2658     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
2659     p.y = parseTwip(tmp);
2660     return p;
2661 }
2662
2663 int parseColor2(const char*str, RGBA*color)
2664 {
2665     int l = strlen(str);
2666     int r,g,b,a;
2667     int t;
2668
2669     struct {unsigned char r,g,b;char*name;} colors[] =
2670     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
2671     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
2672     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
2673     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
2674     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
2675     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
2676     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
2677     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
2678     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
2679     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
2680     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
2681     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
2682
2683     a=255;r=g=b=0;
2684
2685     if(str[0]=='#' && (l==7 || l==9)) {
2686         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
2687             return 0;
2688         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
2689             return 0;
2690         color->r = r; color->g = g; color->b = b; color->a = a;
2691         return 1;
2692     }
2693     int len=strlen(str);
2694     int alpha = 255;
2695     if(strchr(str, '/')) {
2696         len = strchr(str, '/')-str;
2697         sscanf(str+len+1,"%02x", &alpha);
2698     }
2699     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
2700         if(!strncmp(str, colors[t].name, len)) {
2701             r = colors[t].r;
2702             g = colors[t].g;
2703             b = colors[t].b;
2704             a = alpha;
2705             color->r = r; color->g = g; color->b = b; color->a = a;
2706             return 1;
2707         }
2708     return 0;
2709
2710 }
2711 RGBA parseColor(const char*str)
2712 {
2713     RGBA c;
2714     if(!parseColor2(str, &c))
2715         syntaxerror("Expression '%s' is not a color", str);
2716     return c;
2717 }
2718
2719 typedef struct _muladd {
2720     S16 mul;
2721     S16 add;
2722 } MULADD;
2723
2724 MULADD parseMulAdd(const char*str)
2725 {
2726     float add, mul;
2727     char* str2 = (char*)malloc(strlen(str)+5);
2728     int i;
2729     MULADD m;
2730     strcpy(str2, str);
2731     strcat(str2, " 0");
2732     add = 0;
2733     mul = 1.0;
2734     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
2735     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
2736     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
2737     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
2738     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
2739     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
2740     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
2741     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
2742     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
2743     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
2744     else {
2745         syntaxerror("'%s' is not a valid color transform expression", str);
2746     }
2747     m.add = (int)(add*256);
2748     m.mul = (int)(mul*256);
2749     free(str2);
2750     return m;
2751 }
2752
2753 MULADD mergeMulAdd(MULADD m1, MULADD m2)
2754 {
2755     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
2756     double m = ((double)m1.mul*(double)m2.mul)/256.0;
2757     MULADD r;
2758     if(a<-32768) a=-32768;
2759     if(a>32767) a=32767;
2760     if(m<-32768) m=-32768;
2761     if(m>32767) m=32767;
2762     r.add = a;
2763     r.mul = (int)m;
2764     return r;
2765 }
2766
2767 float parsePxOrPercent(const char*fontname, const char*str)
2768 {
2769     int l = strlen(str);
2770     if(strchr(str, '%'))
2771         return parsePercent(str);
2772     if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
2773         float p = atof(str);
2774         return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
2775     }
2776     syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
2777     return 0;
2778 }
2779
2780 float parsePercent(const char*str)
2781 {
2782     int l = strlen(str);
2783     if(!l)
2784         return 1.0;
2785     if(str[l-1]=='%') {
2786         return atof(str)/100.0;
2787     }
2788     syntaxerror("Expression '%s' is not a percentage", str);
2789     return 0;
2790 }
2791 int isPercent(const char*str)
2792 {
2793     return str[strlen(str)-1]=='%';
2794 }
2795 int parseNewSize(const char*str, int size)
2796 {
2797     if(isPercent(str))
2798         return parsePercent(str)*size;
2799     else
2800         return (int)(atof(str)*20);
2801 }
2802
2803 int isColor(char*str)
2804 {
2805     RGBA c;
2806     return parseColor2(str, &c);
2807 }
2808
2809 static const char* lu(map_t* args, char*name)
2810 {
2811     const char* value = map_lookup(args, name);
2812     if(!value) {
2813         map_dump(args, stdout, "");
2814         syntaxerror("internal error 2: value %s should be set", name);
2815     }
2816     return value;
2817 }
2818
2819 static int c_flash(map_t*args)
2820 {
2821     const char* filename = map_lookup(args, "filename");
2822     const char* compressstr = lu(args, "compress");
2823     const char* change_modestr = lu(args, "change-sets-all");
2824     const char* exportstr = lu(args, "export");
2825     SRECT bbox = parseBox(lu(args, "bbox"));
2826     int version = parseInt(lu(args, "version"));
2827     int fps = (int)(parseFloat(lu(args, "fps"))*256);
2828     RGBA color = parseColor(lu(args, "background"));
2829     int compress = 0;
2830
2831     if(!filename || !*filename) {
2832         /* for compatibility */
2833         filename = map_lookup(args, "name");
2834         if(!filename || !*filename) {
2835             filename = 0;
2836         } else {
2837             //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2838             msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2839         }
2840     }
2841
2842     if(!filename || override_outputname)
2843         filename = outputname;
2844
2845     if(!strcmp(compressstr, "default"))
2846         compress = version>=6;
2847     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2848         compress = 1;
2849     else if(!strcmp(compressstr, "no"))
2850         compress = 0;
2851     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2852
2853     if(!strcmp(change_modestr, "yes"))
2854         change_sets_all = 1;
2855     else
2856         if(strcmp(change_modestr, "no"))
2857             syntaxerror("value \"%s\" not supported for the change-sets-all argument", change_modestr);
2858
2859     do_exports=atoi(exportstr);
2860     mainclass=strdup(lu(args, "mainclass"));
2861
2862     s_swf(filename, bbox, version, fps, compress, color);
2863     return 0;
2864 }
2865 int isRelative(const char*str)
2866 {
2867     return !strncmp(str, "<plus>", 6) ||
2868            !strncmp(str, "<minus>", 7);
2869 }
2870 const char* getOffset(const char*str)
2871 {
2872     if(!strncmp(str, "<plus>", 6))
2873         return str+6;
2874     if(!strncmp(str, "<minus>", 7))
2875         return str+7;
2876     syntaxerror("internal error (347)");
2877     return 0;
2878 }
2879 int getSign(const char*str)
2880 {
2881     if(!strncmp(str, "<plus>", 6))
2882         return 1;
2883     if(!strncmp(str, "<minus>", 7))
2884         return -1;
2885     syntaxerror("internal error (348)");
2886     return 0;
2887 }
2888
2889 static dict_t points;
2890 static mem_t mpoints;
2891 static int points_initialized = 0;
2892
2893 static int c_interpolation(map_t *args)
2894 {
2895     int i;
2896     const char* name = lu(args, "name");
2897     if(dict_lookup(&interpolations, name))
2898         syntaxerror("interpolation %s defined twice", name);
2899
2900     interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
2901     const char* functionstr = lu(args, "function");
2902     inter->function = 0;
2903     for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
2904         if(!strcmp(functionstr,interpolationFunctions[i]))
2905         {
2906             inter->function = i + 1;
2907             break;
2908         }
2909     if(!inter->function)
2910         syntaxerror("unkown interpolation function %s", functionstr);
2911     inter->speed = parseFloat(lu(args, "speed"));
2912     inter->amplitude = parseTwip(lu(args, "amplitude"));
2913     inter->growth = parseFloat(lu(args, "growth"));
2914     inter->bounces = parseInt(lu(args, "bounces"));
2915     inter->damping = parseFloat(lu(args, "damping"));
2916     inter->slope = parseFloat(lu(args, "slope"));
2917
2918     dict_put(&interpolations, name, inter);
2919     return 0;
2920 }
2921
2922 SPOINT getPoint(SRECT r, const char*name)
2923 {
2924     int l=0;
2925     if(!strcmp(name, "center")) {
2926         SPOINT p;
2927         p.x = (r.xmin + r.xmax)/2;
2928         p.y = (r.ymin + r.ymax)/2;
2929         return p;
2930     }
2931     if(!strcmp(name, "bottom-center")) {
2932         SPOINT p;
2933         p.x = (r.xmin + r.xmax)/2;
2934         p.y = r.ymax;
2935         return p;
2936     }
2937     if(!strcmp(name, "top-center")) {
2938         SPOINT p;
2939         p.x = (r.xmin + r.xmax)/2;
2940         p.y = r.ymin;
2941         return p;
2942     }
2943     if(!strcmp(name, "top-left")) {
2944         SPOINT p;
2945         p.x = r.xmin;
2946         p.y = r.ymin;
2947         return p;
2948     }
2949     if(!strcmp(name, "top-right")) {
2950         SPOINT p;
2951         p.x = r.xmax;
2952         p.y = r.ymin;
2953         return p;
2954     }
2955     if(!strcmp(name, "bottom-right")) {
2956         SPOINT p;
2957         p.x = r.xmax;
2958         p.y = r.ymax;
2959         return p;
2960     }
2961     if(!strcmp(name, "bottom-left")) {
2962         SPOINT p;
2963         p.x = r.xmin;
2964         p.y = r.ymax;
2965         return p;
2966     }
2967     if(!strcmp(name, "left-center")) {
2968         SPOINT p;
2969         p.x = r.xmin;
2970         p.y = (r.ymin + r.ymax)/2;
2971         return p;
2972     }
2973     if(!strcmp(name, "right-center")) {
2974         SPOINT p;
2975         p.x = r.xmax;
2976         p.y = (r.ymin + r.ymax)/2;
2977         return p;
2978     }
2979
2980
2981     if(points_initialized)
2982         l = (int)dict_lookup(&points, name);
2983     if(l==0) {
2984         syntaxerror("Invalid point: \"%s\".", name);
2985     }
2986     return *(SPOINT*)&mpoints.buffer[l-1];
2987 }
2988
2989
2990 static int texture2(const char*name, const char*object, map_t*args, int errors)
2991 {
2992     SPOINT pos,size;
2993     const char*xstr = map_lookup(args, "x");
2994     const char*ystr = map_lookup(args, "y");
2995     const char*widthstr = map_lookup(args, "width");
2996     const char*heightstr = map_lookup(args, "height");
2997     const char*scalestr = map_lookup(args, "scale");
2998     const char*scalexstr = map_lookup(args, "scalex");
2999     const char*scaleystr = map_lookup(args, "scaley");
3000     const char*rotatestr = map_lookup(args, "rotate");
3001     const char* shearstr = map_lookup(args, "shear");
3002     const char* radiusstr = map_lookup(args, "r");
3003     float x=0,y=0;
3004     float scalex = 1.0, scaley = 1.0;
3005     float rotate=0, shear=0;
3006     float r = 0;
3007     if(!*xstr && !*ystr) {
3008         if(errors)
3009             syntaxerror("x and y must be set");
3010         return 0;
3011     }
3012     if(*scalestr && (*scalexstr || *scaleystr)) {
3013         syntaxerror("scale and scalex/scaley can't both be set");
3014         return 0;
3015     }
3016     if((*widthstr || *heightstr) && *radiusstr) {
3017         syntaxerror("width/height and radius can't both be set");
3018     }
3019     if(*radiusstr) {
3020         widthstr = radiusstr;
3021         heightstr = radiusstr;
3022     }
3023     if(!*xstr) xstr="0";
3024     if(!*ystr) ystr="0";
3025     if(!*rotatestr) rotatestr="0";
3026     if(!*shearstr) shearstr="0";
3027
3028     if(*scalestr) {
3029         scalex = scaley = parsePercent(scalestr);
3030     } else if(*scalexstr || *scaleystr) {
3031         if(scalexstr) scalex = parsePercent(scalexstr);
3032         if(scaleystr) scaley = parsePercent(scaleystr);
3033     } else if(*widthstr || *heightstr) {
3034         int width=0;
3035         int height=0;
3036         s_getBitmapSize(object, &width, &height);
3037         if(*widthstr)
3038             scalex = (float)parseTwip(widthstr)/(float)width;
3039         if(*heightstr)
3040             scaley = (float)parseTwip(heightstr)/(float)height;
3041     }
3042     x = parseTwip(xstr);
3043     y = parseTwip(ystr);
3044     rotate = parseFloat(rotatestr);
3045     shear = parseFloat(shearstr);
3046
3047     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
3048
3049     return 0;
3050 }
3051
3052 static int c_texture(map_t*args)
3053 {
3054     const char*name = lu(args, "instance");
3055     const char*object = lu(args, "character");
3056     return texture2(name, object, args, 1);
3057 }
3058
3059 static int c_gradient(map_t*args)
3060 {
3061     const char*name = lu(args, "name");
3062     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
3063     int rotate = parseInt(lu(args, "rotate"));
3064
3065     readToken();
3066     if(type != RAWDATA)
3067         syntaxerror("colon (:) expected");
3068
3069     if(dict_lookup(&gradients, name))
3070         syntaxerror("gradient %s defined twice", name);
3071
3072     s_gradient(name, text, radial, rotate);
3073
3074     /* check whether we also have placement information,
3075        which would make this a positioned gradient.
3076        If there is placement information, texture2() will
3077        add a texture, which has priority over the gradient.
3078      */
3079     texture2(name, name, args, 0);
3080     return 0;
3081 }
3082
3083 static const char* checkFiltername(map_t* args)
3084 {
3085     const char* name = lu(args, "name");
3086     if(strchr(name, ','))
3087         syntaxerror("the comma (,) is used to separate filters in filterlists. Please do not use in filternames.");
3088     return name;
3089 }
3090
3091 static int c_blur(map_t*args)
3092 {
3093     const char*name = checkFiltername(args);
3094     const char*blurstr = lu(args, "blur");
3095     const char*blurxstr = lu(args, "blurx");
3096     const char*blurystr = lu(args, "blury");
3097     float blurx=1.0, blury=1.0;
3098     if(blurstr[0]) {
3099         blurx = parseFloat(blurstr);
3100         blury = parseFloat(blurstr);
3101     }
3102     if(blurxstr[0])
3103         blurx = parseFloat(blurxstr);
3104     if(blurystr[0])
3105         blury = parseFloat(blurystr);
3106     int passes = parseInt(lu(args, "passes"));
3107     s_blur(name, blurx, blury, passes);
3108     return 0;
3109 }
3110
3111 static int c_gradientglow(map_t*args)
3112 {
3113     const char*name = checkFiltername(args);
3114     const char*gradient = lu(args, "gradient");
3115     const char*blurstr = lu(args, "blur");
3116     const char*blurxstr = lu(args, "blurx");
3117     const char*blurystr = lu(args, "blury");
3118     float blurx=1.0, blury=1.0;
3119     if(blurstr[0]) {
3120         blurx = parseFloat(blurstr);
3121         blury = parseFloat(blurstr);
3122     }
3123     if(blurxstr[0])
3124         blurx = parseFloat(blurxstr);
3125     if(blurystr[0])
3126         blury = parseFloat(blurystr);
3127
3128     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
3129     float distance = parseFloat(lu(args, "distance"));
3130     float strength = parseFloat(lu(args, "strength"));
3131     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
3132     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
3133     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
3134     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
3135     int passes = parseInt(lu(args, "passes"));
3136
3137     s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
3138     return 0;
3139 }
3140
3141 static int c_dropshadow(map_t*args)
3142 {
3143     const char*name = checkFiltername(args);
3144     RGBA color = parseColor(lu(args, "color"));
3145     const char*blurstr = lu(args, "blur");
3146     const char*blurxstr = lu(args, "blurx");
3147     const char*blurystr = lu(args, "blury");
3148     float blurx=1.0, blury=1.0;
3149     if(blurstr[0]) {
3150         blurx = parseFloat(blurstr);
3151         blury = parseFloat(blurstr);
3152     }
3153     if(blurxstr[0])
3154         blurx = parseFloat(blurxstr);
3155     if(blurystr[0])
3156         blury = parseFloat(blurystr);
3157
3158     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
3159     float distance = parseFloat(lu(args, "distance"));
3160     float strength = parseFloat(lu(args, "strength"));
3161     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
3162     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
3163     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
3164     int passes = parseInt(lu(args, "passes"));
3165
3166     s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
3167     return 0;
3168 }
3169
3170 static int c_bevel(map_t*args)
3171 {
3172     const char*name = checkFiltername(args);
3173     RGBA shadow = parseColor(lu(args, "shadow"));
3174     RGBA highlight = parseColor(lu(args, "highlight"));
3175     const char*blurstr = lu(args, "blur");
3176     const char*blurxstr = lu(args, "blurx");
3177     const char*blurystr = lu(args, "blury");
3178     float blurx=1.0, blury=1.0;
3179     if(blurstr[0]) {
3180         blurx = parseFloat(blurstr);
3181         blury = parseFloat(blurstr);
3182     }
3183     if(blurxstr[0])
3184         blurx = parseFloat(blurxstr);
3185     if(blurystr[0])
3186         blury = parseFloat(blurystr);
3187
3188     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
3189     float distance = parseFloat(lu(args, "distance"));
3190     float strength = parseFloat(lu(args, "strength"));
3191     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
3192     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
3193     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
3194     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
3195     int passes = parseInt(lu(args, "passes"));
3196
3197     s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
3198     return 0;
3199 }
3200
3201 static int c_define(map_t*args)
3202 {
3203     const char*name = lu(args, "name");
3204     const char*value = lu(args, "value");
3205     
3206     if(!defines_initialized) {
3207         dict_init(&defines, 16);
3208         mem_init(&define_values);
3209         defines_initialized = 1;
3210     }
3211     int val = parseTwip(value);
3212     int pos = mem_put(&define_values, &val, sizeof(val));
3213     dict_put(&defines, name, (void*)(pos+1));
3214     return 0;
3215 }
3216 static int c_point(map_t*args)
3217 {
3218     const char*name = lu(args, "name");
3219     int pos;
3220     SPOINT p;
3221     if(!points_initialized) {
3222         dict_init(&points, 16);
3223         mem_init(&mpoints);
3224         points_initialized = 1;
3225     }
3226     p.x = parseTwip(lu(args, "x"));
3227     p.y = parseTwip(lu(args, "y"));
3228     pos = mem_put(&mpoints, &p, sizeof(p));
3229     dict_put(&points, name, (void*)(pos+1));
3230     return 0;
3231 }
3232 static int c_play(map_t*args)
3233 {
3234     const char*name = lu(args, "name");
3235     const char*loop = lu(args, "loop");
3236     const char*nomultiple = lu(args, "nomultiple");
3237     int nm = 0;
3238     if(!strcmp(nomultiple, "nomultiple"))
3239         nm = 1;
3240     else
3241         nm = parseInt(nomultiple);
3242
3243     if(s_playsound(name, parseInt(loop), nm, 0)) {
3244         return 0;
3245     } else if(s_swf3action(name, "play")) {
3246         return 0;
3247     }
3248     return 0;
3249 }
3250
3251 static int c_stop(map_t*args)
3252 {
3253     const char*name = map_lookup(args, "name");
3254
3255     if(s_playsound(name, 0,0,1))
3256         return 0;
3257     else if(s_swf3action(name, "stop"))
3258         return 0;
3259     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
3260         return 0;
3261 }
3262
3263 static int c_nextframe(map_t*args)
3264 {
3265     const char*name = lu(args, "name");
3266
3267     if(s_swf3action(name, "nextframe")) {
3268         return 0;
3269     }
3270     syntaxerror("I don't know anything about movie \"%s\"", name);
3271     return 0;
3272 }
3273
3274 static int c_previousframe(map_t*args)
3275 {
3276     const char*name = lu(args, "name");
3277
3278     if(s_swf3action(name, "previousframe")) {
3279         return 0;
3280     }
3281     syntaxerror("I don't know anything about movie \"%s\"", name);
3282     return 0;
3283 }
3284
3285 static int c_movement(map_t*args, int type)
3286 {
3287     const char*instance = lu(args, "name");
3288
3289     const char* xstr="";
3290     const char* ystr="";
3291     SRECT oldbbox;
3292     parameters_t p;
3293     U16 set = 0x0000;
3294
3295     xstr = lu(args, "x");
3296     ystr = lu(args, "y");
3297
3298     s_getParameters(instance, &p);
3299
3300     /* x,y position */
3301     if(xstr[0])
3302     {
3303         if(isRelative(xstr))
3304         {
3305             if(type == PT_PUT || type == PT_STARTCLIP)
3306                 syntaxerror("relative x values not allowed for initial put or startclip");
3307             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3308         }
3309         else
3310         {
3311             p.x = parseTwip(xstr);
3312         }
3313         set = set | SF_X;
3314      }
3315     if(ystr[0])
3316     {
3317         if(isRelative(ystr))
3318         {
3319             if(type == PT_PUT || type == PT_STARTCLIP)
3320                 syntaxerror("relative y values not allowed for initial put or startclip");
3321             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3322         }
3323         else
3324         {
3325             p.y = parseTwip(ystr);
3326         }
3327         set = set | SF_Y;
3328     }
3329
3330     if(change_sets_all)
3331         set = SF_ALL;
3332     p.set = set;
3333
3334     switch (type)
3335     {
3336         case PT_MOVE:
3337             {
3338                 const char* interstr = lu(args, "interpolation");
3339                 interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
3340                 if(!inter)
3341                     syntaxerror("unkown interpolation %s", interstr);
3342                 s_change(instance, p, inter);
3343             }
3344             break;
3345         case PT_SMOVE:
3346             {
3347                 const char* interstr = lu(args, "interpolation");
3348                 interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
3349                 if(!inter)
3350                     syntaxerror("unkown interpolation %s", interstr);
3351                 s_schange(instance, p, inter);
3352             }
3353             break;
3354         case PT_SWEEP:
3355             {
3356                 const char* rstr = lu(args, "r");
3357                 int radius = parseTwip(rstr);
3358                 if(radius <= 0)
3359                         syntaxerror("sweep not possible: radius must be greater than 0.");
3360                 const char* dirstr = lu(args, "dir");
3361                 int clockwise = parseDir(dirstr);
3362                 const char* arcstr = lu(args, "arc");
3363                 int short_arc = parseArc(arcstr);
3364                 const char* interstr = lu(args, "interpolation");
3365                 interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
3366                 if(!inter)
3367                     syntaxerror("unkown interpolation %s", interstr);
3368                 s_sweep(instance, p, radius, clockwise, short_arc, inter);
3369             }
3370             break;
3371         }
3372     return 0;
3373 }
3374
3375 static int c_placement(map_t*args, int type)
3376 {
3377     const char*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
3378     const char*character = 0;
3379
3380     const char* luminancestr = lu(args, "luminance");
3381     const char* scalestr = lu(args, "scale");
3382     const char* scalexstr = lu(args, "scalex");
3383     const char* scaleystr = lu(args, "scaley");
3384     const char* rotatestr = lu(args, "rotate");
3385     const char* shearstr = lu(args, "shear");
3386     const char* xstr="", *pivotstr="";
3387     const char* ystr="", *anglestr="";
3388     const char*above = lu(args, "above"); /*FIXME*/
3389     const char*below = lu(args, "below");
3390     const char* rstr = lu(args, "red");
3391     const char* gstr = lu(args, "green");
3392     const char* bstr = lu(args, "blue");
3393     const char* astr = lu(args, "alpha");
3394     const char* pinstr = lu(args, "pin");
3395     const char* as = map_lookup(args, "as");
3396     const char* blendmode = lu(args, "blend");
3397     const char* filterstr = lu(args, "filter");
3398     U8 blend;
3399     MULADD r,g,b,a;
3400     float oldwidth;
3401     float oldheight;
3402     SRECT oldbbox;
3403     MULADD luminance;
3404     parameters_t p;
3405     U16 set = 0x0000;
3406
3407     if(type==9)
3408     { // (?) .rotate  or .arcchange
3409         pivotstr = lu(args, "pivot");
3410         anglestr = lu(args, "angle");
3411     }
3412     else
3413     {
3414         xstr = lu(args, "x");
3415         ystr = lu(args, "y");
3416     }
3417
3418     if(luminancestr[0])
3419         luminance = parseMulAdd(luminancestr);
3420     else
3421     {
3422         luminance.add = 0;
3423         luminance.mul = 256;
3424     }
3425
3426     if(scalestr[0])
3427     {
3428         if(scalexstr[0]||scaleystr[0])
3429             syntaxerror("scalex/scaley and scale cannot both be set");
3430         scalexstr = scaleystr = scalestr;
3431     }
3432
3433     if(type == PT_PUT || type == PT_STARTCLIP)  {
3434         // put or startclip
3435         character = lu(args, "character");
3436         parameters_clear(&p);
3437     } else if(type == PT_BUTTON) {
3438         character = lu(args, "name");
3439         parameters_clear(&p);
3440         // button's show
3441     } else {
3442         s_getParameters(instance, &p);
3443     }
3444
3445     /* x,y position */
3446     if(xstr[0])
3447     {
3448         if(isRelative(xstr))
3449         {
3450             if(type == PT_PUT || type == PT_STARTCLIP)
3451                 syntaxerror("relative x values not allowed for initial put or startclip");
3452             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3453         }
3454         else
3455         {
3456             p.x = parseTwip(xstr);
3457         }
3458         set = set | SF_X;
3459      }
3460     if(ystr[0])
3461     {
3462         if(isRelative(ystr))
3463         {
3464             if(type == PT_PUT || type == PT_STARTCLIP)
3465                 syntaxerror("relative y values not allowed for initial put or startclip");
3466             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3467         }
3468         else
3469         {
3470             p.y = parseTwip(ystr);
3471         }
3472         set = set | SF_Y;
3473         }
3474
3475     /* scale, scalex, scaley */
3476     if(character)
3477         oldbbox = s_getCharBBox(character);
3478     else
3479         oldbbox = s_getInstanceBBox(instance);
3480     oldwidth = oldbbox.xmax - oldbbox.xmin;
3481     oldheight = oldbbox.ymax - oldbbox.ymin;
3482     if(scalexstr[0])
3483     {
3484         if(oldwidth==0)
3485             p.scalex = 1.0;
3486         else
3487             if(scalexstr[0])
3488                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
3489         set = set | SF_SCALEX;
3490     }
3491    if(scaleystr[0])
3492    {
3493         if(oldheight==0)
3494             p.scaley = 1.0;
3495         else
3496             if(scaleystr[0])
3497                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
3498         set = set | SF_SCALEY;
3499    }
3500
3501     /* rotation */
3502     if(rotatestr[0])
3503     {
3504         if(isRelative(rotatestr))
3505             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
3506         else
3507             p.rotate = parseFloat(rotatestr);
3508         set = set | SF_ROTATE;
3509         }
3510
3511     /* shearing */
3512     if(shearstr[0])
3513     {
3514         if(isRelative(shearstr))
3515             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
3516         else
3517             p.shear = parseFloat(shearstr);
3518         set = set | SF_SHEAR;
3519     }
3520
3521     if(pivotstr[0])
3522     {
3523         if(isPoint(pivotstr))
3524             p.pivot = parsePoint(pivotstr);
3525         else
3526             p.pivot = getPoint(oldbbox, pivotstr);
3527         set = set | SF_PIVOT;
3528     }
3529
3530     if(pinstr[0])
3531     {
3532         if(isPoint(pinstr))
3533             p.pin = parsePoint(pinstr);
3534         else
3535             p.pin = getPoint(oldbbox, pinstr);
3536         set = set | SF_PIN;
3537     }
3538
3539     /* color transform */
3540
3541     if(rstr[0] || luminancestr[0])
3542     {
3543         MULADD r;
3544         if(rstr[0])
3545             r = parseMulAdd(rstr);
3546         else
3547         {
3548             r.add = p.cxform.r0;
3549             r.mul = p.cxform.r1;
3550         }
3551         r = mergeMulAdd(r, luminance);
3552         p.cxform.r0 = r.mul;
3553         p.cxform.r1 = r.add;
3554         set = set | SF_CX_R;
3555         }
3556     if(gstr[0] || luminancestr[0])
3557     {
3558         MULADD g;
3559         if(gstr[0])
3560             g = parseMulAdd(gstr);
3561         else
3562         {
3563             g.add = p.cxform.g0;
3564             g.mul = p.cxform.g1;
3565         }
3566         g = mergeMulAdd(g, luminance);
3567         p.cxform.g0 = g.mul;
3568         p.cxform.g1 = g.add;
3569         set = set | SF_CX_G;
3570     }
3571     if(bstr[0] || luminancestr[0])
3572     {
3573         MULADD b;
3574         if(bstr[0])
3575             b = parseMulAdd(bstr);
3576         else
3577         {
3578             b.add = p.cxform.b0;
3579             b.mul = p.cxform.b1;
3580         }
3581         b = mergeMulAdd(b, luminance);
3582         p.cxform.b0 = b.mul;
3583         p.cxform.b1 = b.add;
3584         set = set | SF_CX_B;
3585     }
3586     if(astr[0])
3587     {
3588         MULADD a = parseMulAdd(astr);
3589         p.cxform.a0 = a.mul;
3590         p.cxform.a1 = a.add;
3591         set = set | SF_CX_A;
3592     }
3593
3594     if(blendmode[0])
3595     {
3596         int t;
3597         blend = 255;
3598         for(t = 0; blendModeNames[t]; t++)
3599         {
3600             if(!strcmp(blendModeNames[t], blendmode))
3601             {
3602                 blend = t;
3603                 break;
3604             }
3605         }
3606         if(blend == 255)
3607         {
3608             syntaxerror("unknown blend mode: '%s'", blendmode);
3609         }
3610         p.blendmode = blend;
3611         set = set | SF_BLEND;
3612         }
3613
3614     if(filterstr[0])
3615     {
3616         p.filters = parseFilters((char*)filterstr);
3617         set = set | SF_FILTER;
3618     }
3619
3620     if(type == PT_CHANGE && set & (SF_X | SF_Y))
3621         warning("As of version 0.8.2 using the .change command to modify an \
3622 object's position on the stage is considered deprecated. Future \
3623 versions may consider x and y parameters for the .change command \
3624 to be illegal; please use the .move command.");
3625
3626     if(change_sets_all)
3627         set = SF_ALL;
3628     p.set = set;
3629
3630     switch (type)
3631     {
3632         case PT_PUT:
3633             s_put(instance, character, p);
3634             break;
3635         case PT_CHANGE:
3636             {
3637                 const char* interstr = lu(args, "interpolation");
3638                 interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
3639                 if(!inter)
3640                     syntaxerror("unkown interpolation %s", interstr);
3641                 s_change(instance, p, inter);
3642             }
3643             break;
3644         case PT_SCHANGE:
3645             {
3646                 const char* interstr = lu(args, "interpolation");
3647                 interpolation_t* inter = (interpolation_t*)dict_lookup(&interpolations, interstr);
3648                 if(!inter)
3649                     syntaxerror("unkown interpolation %s", interstr);
3650                 s_schange(instance, p, inter);
3651             }
3652             break;
3653         case PT_JUMP:
3654             s_jump(instance, p);
3655             break;
3656         case PT_STARTCLIP:
3657             s_startclip(instance, character, p);
3658             break;
3659         case PT_BUTTON:
3660             if(as && as[0])
3661                 s_buttonput(character, as, p);
3662             else
3663                 s_buttonput(character, "shape", p);
3664             break;
3665 //        default:
3666         }
3667     return 0;
3668 }
3669 static int c_put(map_t*args)
3670 {
3671     c_placement(args, PT_PUT);
3672     return 0;
3673 }
3674 static int c_change(map_t*args)
3675 {
3676     if(currentframe == 0)
3677         warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
3678     c_placement(args, PT_CHANGE);
3679     return 0;
3680 }
3681 static int c_schange(map_t*args)
3682 {
3683     c_placement(args, PT_SCHANGE);
3684     return 0;
3685 }
3686 static int c_move(map_t* args)
3687 {
3688     c_movement(args, PT_MOVE);
3689     return 0;
3690 }
3691 static int c_smove(map_t* args)
3692 {
3693     c_movement(args, PT_SMOVE);
3694     return 0;
3695 }
3696 static int c_sweep(map_t* args)
3697 {
3698     c_movement(args, PT_SWEEP);
3699     return 0;
3700 }
3701 static int c_arcchange(map_t*args)
3702 {
3703     c_placement(args, 0);
3704     return 0;
3705 }
3706 static int c_jump(map_t*args)
3707 {
3708     c_placement(args, PT_JUMP);
3709     return 0;
3710 }
3711 static int c_startclip(map_t*args)
3712 {
3713     c_placement(args, PT_STARTCLIP);
3714     return 0;
3715 }
3716 static int c_show(map_t*args)
3717 {
3718     c_placement(args, PT_BUTTON);
3719     return 0;
3720 }
3721 static int c_toggle(map_t* args)
3722 {
3723     const char*instance = lu(args, "name");
3724     U16 flagsOn = 0x0000, flagsOff = 0xffff;
3725     const char* alignstr = lu(args, "fixed_alignment");
3726     if(!strcmp(alignstr, "on"))
3727         flagsOn += IF_FIXED_ALIGNMENT;
3728     else
3729         if(!strcmp(alignstr, "off"))
3730             flagsOff -= IF_FIXED_ALIGNMENT;
3731         else
3732             syntaxerror("values for toggle must be \"on\" or \"off\". %s is not legal.", alignstr);
3733     s_toggle(instance, flagsOn, flagsOff);
3734     return 0;
3735 }
3736 static int c_del(map_t*args)
3737 {
3738     const char*instance = lu(args, "name");
3739     s_delinstance(instance);
3740     return 0;
3741 }
3742 static int c_end(map_t*args)
3743 {
3744     s_end();
3745     return 0;
3746 }
3747 static int c_sprite(map_t*args)
3748 {
3749     const char* name = lu(args, "name");
3750     const char* scalinggrid = lu(args, "scalinggrid");
3751     const char* as3name = lu(args, "as3name");
3752
3753     if(scalinggrid && *scalinggrid) {
3754         SRECT r = parseBox(scalinggrid);
3755         s_sprite(name, &r, as3name);
3756     } else {
3757         s_sprite(name, 0, as3name);
3758     }
3759     return 0;
3760 }
3761 static int c_frame(map_t*args)
3762 {
3763     const char*framestr = lu(args, "n");
3764     const char*cutstr = lu(args, "cut");
3765
3766     const char*name = lu(args, "name");
3767     const char*anchor = lu(args, "anchor");
3768     char buf[40];
3769
3770     if(!strcmp(anchor, "anchor") && !*name)
3771         name = framestr;
3772
3773     int frame;
3774     int cut = 0;
3775     if(strcmp(cutstr, "no"))
3776         cut = 1;
3777     if(isRelative(framestr)) {
3778         frame = s_getframe();
3779         if(getSign(framestr)<0)
3780             syntaxerror("relative frame expressions must be positive");
3781         frame += parseInt(getOffset(framestr));
3782     }
3783     else {
3784         frame = parseInt(framestr);
3785         if(s_getframe() >= frame
3786                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
3787             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
3788     }
3789     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
3790     return 0;
3791 }
3792 static int c_primitive(map_t*args)
3793 {
3794     const char*name = lu(args, "name");
3795     const char*command = lu(args, "commandname");
3796     int width=0, height=0, r=0;
3797     int linewidth = parseTwip(lu(args, "line"));
3798     const char*colorstr = lu(args, "color");
3799     RGBA color = parseColor(colorstr);
3800     const char*fillstr = lu(args, "fill");
3801     int dofill = 1;
3802     int type=0;
3803     const char* font;
3804     const char* text;
3805     const char* outline=0;
3806     RGBA fill;
3807     if(!strcmp(command, "circle"))
3808         type = 1;
3809     else if(!strcmp(command, "filled"))
3810         type = 2;
3811
3812     if(type==0) {
3813         width = parseTwip(lu(args, "width"));
3814         height = parseTwip(lu(args, "height"));
3815     } else if(type==1) {
3816         r = parseTwip(lu(args, "r"));
3817     } else if(type==2) {
3818         outline = lu(args, "outline");
3819     }
3820
3821     if(!strcmp(fillstr, "fill"))
3822         fillstr = colorstr;
3823     if(!strcmp(fillstr, "none"))
3824         fillstr = 0;
3825     if(width<0 || height<0 || linewidth<0 || r<0)
3826         syntaxerror("values width, height, line, r must be positive");
3827
3828     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
3829     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
3830     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
3831     return 0;
3832 }
3833
3834 static int c_textshape(map_t*args)
3835 {
3836     const char*name = lu(args, "name");
3837     const char*text = lu(args, "text");
3838     const char*font = lu(args, "font");
3839     float size = parsePxOrPercent(font, lu(args, "size"));
3840
3841     s_textshape(name, font, size, text);
3842     return 0;
3843 }
3844
3845 static int c_swf(map_t*args)
3846 {
3847     const char*name = lu(args, "name");
3848     const char*filename = lu(args, "filename");
3849     const char*command = lu(args, "commandname");
3850     if(!strcmp(command, "shape"))
3851         warning("Please use .swf instead of .shape");
3852     s_includeswf(name, filename);
3853     return 0;
3854 }
3855
3856 static int c_font(map_t*args)
3857 {
3858     const char*name = lu(args, "name");
3859     const char*filename = lu(args, "filename");
3860     s_font(name, filename);
3861     return 0;
3862 }
3863
3864 static int c_sound(map_t*args)
3865 {
3866     const char*name = lu(args, "name");
3867     const char*filename = lu(args, "filename");
3868     s_sound(name, filename);
3869     return 0;
3870 }
3871
3872 static int c_text(map_t*args)
3873 {
3874     const char*name = lu(args, "name");
3875     const char*text = lu(args, "text");
3876     const char*font = lu(args, "font");
3877     float size = parsePxOrPercent(font, lu(args, "size"));
3878     RGBA color = parseColor(lu(args, "color"));
3879     s_text(name, font, text, (int)(size*100), color);
3880     return 0;
3881 }
3882
3883 static int c_soundtrack(map_t*args)
3884 {
3885     return 0;
3886 }
3887
3888 static int c_quicktime(map_t*args)
3889 {
3890     const char*name = lu(args, "name");
3891     const char*url = lu(args, "url");
3892     s_quicktime(name, url);
3893     return 0;
3894 }
3895
3896 static int c_video(map_t*args)
3897 {
3898     const char*name = lu(args, "name");
3899     int width = parseInt(lu(args, "width"));
3900     int height = parseInt(lu(args, "height"));
3901     s_video(name, width, height);
3902     return 0;
3903 }
3904
3905 static int c_image(map_t*args)
3906 {
3907     const char*command = lu(args, "commandname");
3908     const char*name = lu(args, "name");
3909     const char*filename = lu(args, "filename");
3910     if(!strcmp(command,"jpeg")) {
3911         int quality = (int)(parsePercent(lu(args, "quality"))*100);
3912         s_image(name, "jpeg", filename, quality);
3913     } else {
3914         s_image(name, "png", filename, 0);
3915     }
3916     return 0;
3917 }
3918
3919 static int c_outline(map_t*args)
3920 {
3921     const char*name = lu(args, "name");
3922     const char*format = lu(args, "format");
3923
3924     readToken();
3925     if(type != RAWDATA)
3926         syntaxerror("colon (:) expected");
3927
3928     s_outline(name, format, text);
3929     return 0;
3930 }
3931
3932 int fakechar(map_t*args)
3933 {
3934     const char*name = lu(args, "name");
3935     s_box(name, 0, 0, black, 20, 0);
3936     return 0;
3937 }
3938
3939 static int c_egon(map_t*args) {return fakechar(args);}
3940 static int c_button(map_t*args) {
3941     const char*name = lu(args, "name");
3942     const char*as3name = lu(args, "as3name");
3943     s_button(name, as3name);
3944     return 0;
3945 }
3946 static int current_button_flags = 0;
3947 static int c_on_press(map_t*args)
3948 {
3949     const char*position = lu(args, "position");
3950     const char*action = "";
3951     if(!strcmp(position, "inside")) {
3952         current_button_flags |= BC_OVERUP_OVERDOWN;
3953     } else if(!strcmp(position, "outside")) {
3954         //current_button_flags |= BC_IDLE_OUTDOWN;
3955         syntaxerror("IDLE_OVERDOWN not supported by SWF");
3956     } else if(!strcmp(position, "anywhere")) {
3957         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
3958     }
3959     readToken();
3960     if(type == RAWDATA) {
3961         action = text;
3962         s_buttonaction(current_button_flags, action);
3963         current_button_flags = 0;
3964     }
3965     else
3966         pushBack();
3967     return 0;
3968 }
3969 static int c_on_release(map_t*args)
3970 {
3971     const char*position = lu(args, "position");
3972     const char*action = "";
3973     if(!strcmp(position, "inside")) {
3974         current_button_flags |= BC_OVERDOWN_OVERUP;
3975     } else if(!strcmp(position, "outside")) {
3976         current_button_flags |= BC_OUTDOWN_IDLE;
3977     } else if(!strcmp(position, "anywhere")) {
3978         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
3979     }
3980     readToken();
3981     if(type == RAWDATA) {
3982         action = text;
3983         s_buttonaction(current_button_flags, action);
3984         current_button_flags = 0;
3985     }
3986     else
3987         pushBack();
3988     return 0;
3989 }
3990 static int c_on_move_in(map_t*args)
3991 {
3992     const char*position = lu(args, "state");
3993     const char*action = "";
3994     if(!strcmp(position, "pressed")) {
3995         current_button_flags |= BC_OUTDOWN_OVERDOWN;
3996     } else if(!strcmp(position, "not_pressed")) {
3997         current_button_flags |= BC_IDLE_OVERUP;
3998     } else if(!strcmp(position, "any")) {
3999         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
4000     }
4001     readToken();
4002     if(type == RAWDATA) {
4003         action = text;
4004         s_buttonaction(current_button_flags, action);
4005         current_button_flags = 0;
4006     }
4007     else
4008         pushBack();
4009     return 0;
4010 }
4011 static int c_on_move_out(map_t*args)
4012 {
4013     const char*position = lu(args, "state");
4014     const char*action = "";
4015     if(!strcmp(position, "pressed")) {
4016         current_button_flags |= BC_OVERDOWN_OUTDOWN;
4017     } else if(!strcmp(position, "not_pressed")) {
4018         current_button_flags |= BC_OVERUP_IDLE;
4019     } else if(!strcmp(position, "any")) {
4020         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
4021     }
4022     readToken();
4023     if(type == RAWDATA) {
4024         action = text;
4025         s_buttonaction(current_button_flags, action);
4026         current_button_flags = 0;
4027     }
4028     else
4029         pushBack();
4030     return 0;
4031 }
4032 static int c_on_key(map_t*args)
4033 {
4034     const char*key = lu(args, "key");
4035     const char*action = "";
4036     if(strlen(key)==1) {
4037         /* ascii */
4038         if(key[0]>=32) {
4039             current_button_flags |= 0x4000 + (key[0]*0x200);
4040         } else {
4041             syntaxerror("invalid character: %c"+key[0]);
4042             return 1;
4043         }
4044     } else {
4045         /* TODO:
4046            <ctrl-x> = 0x200*(x-'a')
4047            esc = = 0x3600
4048            space = = 0x4000;
4049         */
4050         syntaxerror("invalid key: %s",key);
4051     }
4052     readToken();
4053     if(type == RAWDATA) {
4054         action = text;
4055         s_buttonaction(current_button_flags, action);
4056         current_button_flags = 0;
4057     }
4058     else
4059         pushBack();
4060     return 0;
4061 }
4062
4063 static int c_edittext(map_t*args)
4064 {
4065  //"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"},
4066     const char*name = lu(args, "name");
4067     const char*font = lu(args, "font");
4068     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
4069     int width = parseTwip(lu(args, "width"));
4070     int height = parseTwip(lu(args, "height"));
4071     const char*text  = lu(args, "text");
4072     RGBA color = parseColor(lu(args, "color"));
4073     int maxlength = parseInt(lu(args, "maxlength"));
4074     const char*variable = lu(args, "variable");
4075     const char*passwordstr = lu(args, "password");
4076     const char*wordwrapstr = lu(args, "wordwrap");
4077     const char*multilinestr = lu(args, "multiline");
4078     const char*htmlstr = lu(args, "html");
4079     const char*noselectstr = lu(args, "noselect");
4080     const char*readonlystr = lu(args, "readonly");
4081     const char*borderstr = lu(args, "border");
4082     const char*autosizestr = lu(args, "autosize");
4083     const char*alignstr = lu(args, "align");
4084     int align = -1;
4085
4086     int flags = 0;
4087     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
4088     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
4089     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
4090     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
4091     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
4092     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
4093     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
4094     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
4095     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
4096     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
4097     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
4098     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
4099     else syntaxerror("Unknown alignment: %s", alignstr);
4100
4101     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
4102     return 0;
4103 }
4104
4105 static int c_morphshape(map_t*args) {return fakechar(args);}
4106 static int c_movie(map_t*args) {return fakechar(args);}
4107
4108 static char* readfile(char*filename)
4109 {
4110     FILE*fi = fopen(filename, "rb");
4111     int l;
4112     char*text;
4113     if(!fi)
4114         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
4115     fseek(fi, 0, SEEK_END);
4116     l = ftell(fi);
4117     fseek(fi, 0, SEEK_SET);
4118     text = rfx_alloc(l+1);
4119     fread(text, l, 1, fi);
4120     text[l]=0;
4121     fclose(fi);
4122     return text;
4123 }
4124
4125 static int c_action(map_t*args)
4126 {
4127     const char* filename  = map_lookup(args, "filename");
4128     if(!filename ||!*filename) {
4129         readToken();
4130         if(type != RAWDATA) {
4131             syntaxerror("colon (:) expected");
4132         }
4133         s_action(text);
4134     } else {
4135         s_action(readfile((char*)filename));
4136     }
4137
4138     return 0;
4139 }
4140
4141 static int c_initaction(map_t*args)
4142 {
4143     const char* character = lu(args, "name");
4144     const char* filename  = map_lookup(args, "filename");
4145     if(!filename ||!*filename) {
4146         readToken();
4147         if(type != RAWDATA) {
4148             syntaxerror("colon (:) expected");
4149         }
4150         s_initaction(character, text);
4151     } else {
4152         s_initaction(character, readfile((char*)filename));
4153     }
4154
4155     return 0;
4156 }
4157
4158 static struct {
4159     char*command;
4160     command_func_t* func;
4161     char*arguments;
4162 } arguments[] =
4163 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no @export=1 @mainclass="},
4164  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
4165  // "import" type stuff
4166  {"swf", c_swf, "name filename"},
4167  {"shape", c_swf, "name filename"},
4168  {"jpeg", c_image, "name filename quality=80%"},
4169  {"png", c_image, "name filename"},
4170  {"movie", c_movie, "name filename"},
4171  {"sound", c_sound, "name filename"},
4172  {"font", c_font, "name filename glyphs= @flashtype="},
4173  {"soundtrack", c_soundtrack, "filename"},
4174  {"quicktime", c_quicktime, "url"},
4175  {"video", c_video, "name width= height="},
4176
4177     // generators of primitives
4178
4179  {"define", c_define, "name value=0"},
4180  {"point", c_point, "name x=0 y=0"},
4181  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
4182  {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5 damping=2 slope=0"},
4183  {"outline", c_outline, "name format=simple"},
4184  {"textshape", c_textshape, "name font size=100% text"},
4185
4186     // filters
4187  {"blur", c_blur, "name blur= blurx= blury= passes=1"},
4188  {"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"},
4189  {"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"},
4190  {"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"},
4191
4192     // character generators
4193  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
4194  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
4195  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
4196
4197  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
4198  {"text", c_text, "name text font size=100% color=white"},
4199  {"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="},
4200  {"morphshape", c_morphshape, "name start end"},
4201  {"button", c_button, "name as3name="},
4202     {"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="},
4203     {"on_press", c_on_press, "position=inside"},
4204     {"on_release", c_on_release, "position=anywhere"},
4205     {"on_move_in", c_on_move_in, "state=not_pressed"},
4206     {"on_move_out", c_on_move_out, "state=not_pressed"},
4207     {"on_key", c_on_key, "key=any"},
4208
4209     // control tags
4210  {"play", c_play, "name loop=0 @nomultiple=0"},
4211  {"stop", c_stop, "name= "},
4212  {"nextframe", c_nextframe, "name"},
4213  {"previousframe", c_previousframe, "name"},
4214
4215     // object placement tags
4216  {"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="},
4217  {"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="},
4218  {"move", c_move,       "name x= y= interpolation=linear"},
4219  {"smove", c_smove,     "name x= y= interpolation=linear"},
4220  {"sweep", c_sweep,     "name x= y= r= dir=counterclockwise arc=short interpolation=linear"},
4221  {"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"},
4222  //{"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
4223  {"schange", c_schange, "name red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
4224  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
4225  {"del", c_del, "name"},
4226     // virtual object placement
4227  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
4228     // switching
4229  {"toggle", c_toggle, "name fixed_alignment="},
4230
4231     // commands which start a block
4232 //startclip (see above)
4233  {"sprite", c_sprite, "name scalinggrid= as3name="},
4234  {"action", c_action, "filename="},
4235  {"initaction", c_initaction, "name filename="},
4236
4237  {"end", c_end, ""}
4238 };
4239
4240
4241 static map_t parseArguments(char*command, char*pattern)
4242 {
4243     char*x;
4244     char*d,*e;
4245
4246     string_t name[64];
4247     string_t value[64];
4248     int set[64];
4249     int isboolean[64];
4250     int pos;
4251     int len;
4252     int t;
4253     string_t t1,t2;
4254     map_t result;
4255     map_init(&result);
4256
4257     string_set(&t1, "commandname");
4258     string_set(&t2, command);
4259     map_put(&result, t1, t2);
4260
4261     if(!pattern || !*pattern)
4262         return result;
4263
4264     x = pattern;
4265
4266     pos = 0;
4267
4268     if(!strncmp("<i> ", x, 3)) {
4269         readToken();
4270         if(type == COMMAND || type == RAWDATA) {
4271             pushBack();
4272             syntaxerror("character name expected");
4273         }
4274         name[pos].str = "instance";
4275         name[pos].len = 8;
4276         value[pos].str = text;
4277         value[pos].len = strlen(text);
4278         set[pos] = 1;
4279         pos++;
4280
4281         if(type == ASSIGNMENT)
4282             readToken();
4283
4284         name[pos].str = "character";
4285         name[pos].len = 9;
4286         value[pos].str = text;
4287         value[pos].len = strlen(text);
4288         set[pos] = 1;
4289         pos++;
4290
4291         x+=4;
4292     }
4293
4294     while(*x) {
4295         isboolean[pos] = (x[0] =='@');
4296         if(isboolean[pos])
4297             x++;
4298
4299         d = strchr(x, ' ');
4300         e = strchr(x, '=');
4301         if(!d)
4302             d=&x[strlen(x)];
4303         set[pos] = 0;
4304
4305         if(!e || d<e) {
4306             // no default
4307             name[pos].str = x;
4308             name[pos].len = d-x;
4309             value[pos].str = 0;
4310             value[pos].len = 0;
4311         } else {
4312             name[pos].str = x;
4313             name[pos].len = e-x;
4314             value[pos].str = e+1;
4315             value[pos].len = d-e-1;
4316         }
4317         pos++;
4318         if(!*d) break;
4319         x=d+1;
4320     }
4321     len = pos;
4322
4323 /*    for(t=0;t<len;t++) {
4324         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
4325                 isboolean[t]?"(boolean)":"");
4326     }*/
4327
4328     while(1) {
4329         readToken();
4330         if(type == RAWDATA || type == COMMAND) {
4331             pushBack();
4332             break;
4333         }
4334
4335         // first, search for boolean arguments
4336         for(pos=0;pos<len;pos++)
4337         {
4338             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
4339                 set[pos] = 1;
4340                 if(type == ASSIGNMENT)
4341                     readToken();
4342                 value[pos].str = text;
4343                 value[pos].len = strlen(text);
4344                 /*printf("setting boolean parameter %s (to %s)\n",
4345                         strdup_n(name[pos], namelen[pos]),
4346                         strdup_n(value[pos], valuelen[pos]));*/
4347                 break;
4348             }
4349         }
4350
4351         // second, search for normal arguments
4352         if(pos==len)
4353         for(pos=0;pos<len;pos++)
4354         {
4355             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
4356                (type != ASSIGNMENT && !set[pos])) {
4357                 if(set[pos]) {
4358                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
4359                 }
4360                 if(type == ASSIGNMENT)
4361                     readToken();
4362                 set[pos] = 1;
4363                 value[pos].str = text;
4364                 value[pos].len = strlen(text);
4365 #if 0//def DEBUG
4366                 printf("setting parameter %s (to %s)\n",
4367                         strdup_n(name[pos].str, name[pos].len),
4368                         strdup_n(value[pos].str, value[pos].len));
4369 #endif
4370                 break;
4371             }
4372         }
4373         if(pos==len) {
4374             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
4375         }
4376     }
4377 #if 0//def DEBUG
4378     for(t=0;t<len;t++) {
4379         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
4380     }
4381 #endif
4382     for(t=0;t<len;t++) {
4383         if(value[t].str && value[t].str[0] == '*') {
4384             //relative default- take value from some other parameter
4385             int s;
4386             for(s=0;s<len;s++) {
4387                 if(value[s].len == value[t].len-1 &&
4388                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
4389                     value[t].str = value[s].str;
4390             }
4391         }
4392         if(value[t].str == 0) {
4393             pushBack();
4394             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
4395         }
4396     }
4397
4398     /* ok, now construct the dictionary from the parameters */
4399
4400     for(t=0;t<len;t++)
4401     {
4402         map_put(&result, name[t], value[t]);
4403     }
4404     return result;
4405 }
4406 static void parseArgumentsForCommand(char*command)
4407 {
4408     int t;
4409     map_t args;
4410     int nr = -1;
4411     msg("<verbose> parse Command: %s (line %d)", command, line);
4412
4413     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
4414         if(!strcmp(arguments[t].command, command)) {
4415
4416             /* ugly hack- will be removed soon (once documentation and .sc generating
4417                utilities have been changed) */
4418             if(!strcmp(command, "swf") && !stackpos) {
4419                 warning("Please use .flash instead of .swf- this will be mandatory soon");
4420                 command = "flash";
4421                 t = 0;
4422             }
4423
4424             args = parseArguments(command, arguments[t].arguments);
4425             nr = t;
4426             break;
4427         }
4428     }
4429     if(nr<0)
4430         syntaxerror("command %s not known", command);
4431
4432 #ifndef EMPTY
4433     // catch missing .flash directives at the beginning of a file
4434     if(strcmp(command, "flash") && !stackpos)
4435     {
4436         syntaxerror("No movie defined- use .flash first");
4437     }
4438 #endif
4439
4440 #ifdef DEBUG
4441     printf(".%s\n", command);fflush(stdout);
4442     map_dump(&args, stdout, "\t");fflush(stdout);
4443 #endif
4444
4445 #ifndef EMPTY
4446     (*arguments[nr].func)(&args);
4447 #else
4448     if(!strcmp(command, "action")  || !strcmp(command, "initaction") ||
4449        !strcmp(command, "outline") || !strcmp(command, "gradient")) {
4450         readToken();
4451         if(type != RAWDATA) {
4452             syntaxerror("colon (:) expected");
4453         }
4454     }
4455 #endif
4456     map_clear(&args);
4457     return;
4458 }
4459
4460
4461 /* for now only intended to find what glyphs of each font are to be included in the swf file.
4462  * Therefore some knowledge about the command is assumed i.e. it is font/text-related
4463  * No syntax checking is done */
4464 static void analyseArgumentsForCommand(char*command)
4465 {
4466     int t;
4467     map_t args;
4468     int nr = -1;
4469     U8* glyphs_to_include;
4470     msg("<verbose> analyse Command: %s (line %d)", command, line);
4471
4472     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++)
4473     {
4474         if(!strcmp(arguments[t].command, command))
4475         {
4476             args = parseArguments(command, arguments[t].arguments);
4477             nr = t;
4478             break;
4479         }
4480     }
4481 #ifdef DEBUG
4482     printf(".%s\n", command);fflush(stdout);
4483     map_dump(&args, stdout, "\t");fflush(stdout);
4484 #endif
4485     const char* name = lu(&args, "name");
4486     if(!strcmp(command, "font"))
4487     {
4488         const char* fontfile = lu(&args, "filename");
4489         const char* glyphs = lu(&args, "glyphs");
4490         const char* flashtype = lu(&args, "flashtype");
4491         s_createfont(name, fontfile, glyphs, flashtype[0]);
4492     } else {
4493         SWFFONT* font = dict_lookup(&fonts, lu(&args, "font"));
4494         if(!font) {
4495             //that's ok... it might be an edittext with a system font
4496             //syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
4497         } else
4498             if(font->use && !font->use->glyphs_specified) {
4499                 if(!strcmp(command, "edittext"))
4500                 {
4501                     swf_FontUseAll(font);
4502                     font->use->glyphs_specified = 1;
4503                 }
4504                 else
4505                     swf_FontUseUTF8(font, (U8*)lu(&args, "text"), 0xffff);
4506             }
4507     }
4508     map_clear(&args);
4509     return;
4510 }
4511
4512 void skipParameters()
4513 {
4514         do
4515                 readToken();
4516         while (type != COMMAND);
4517         pushBack();
4518 }
4519
4520 void findFontUsage()
4521 {
4522     char* fontRelated = "font;text;textshape;edittext;";
4523     while(!noMoreTokens())
4524     {
4525         readToken();
4526         if(type != COMMAND)
4527             syntaxerror("command expected");
4528         if(strstr(fontRelated, text))
4529             analyseArgumentsForCommand(text);
4530         else
4531             if(strcmp(text, "end"))
4532                 skipParameters();
4533     }
4534 }
4535
4536 void firstPass()
4537 {
4538     pos = 0;
4539     id = 0;
4540     dict_init(&fonts, 16);
4541     cleanUp = &freeFontDictionary;
4542     findFontUsage();
4543 }
4544
4545 int main (int argc,char ** argv)
4546 {
4547     int t;
4548     processargs(argc, argv);
4549     initLog(0,-1,0,0,-1,verbose);
4550
4551     if(!filename) {
4552         args_callback_usage(argv[0]);
4553         exit(1);
4554     }
4555
4556     file = generateTokens(filename);
4557     if(!file) {
4558         fprintf(stderr, "parser returned error.\n");
4559         return 1;
4560     }
4561     firstPass();
4562     pos=0;
4563     t=0;
4564
4565     while(!noMoreTokens()) {
4566         readToken();
4567         if(type != COMMAND)
4568             syntaxerror("command expected");
4569         parseArgumentsForCommand(text);
4570     }
4571
4572     s_close();
4573     freeTokens(file);
4574
4575     return 0;
4576 }
4577