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