second half of Huub Schaek's sweep patch
[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     {
2686         SPOINT p;
2687         p.x = (r.xmin + r.xmax)/2;
2688         p.y = r.ymax;
2689         return p;
2690     }
2691
2692
2693     if(points_initialized)
2694         l = (int)dictionary_lookup(&points, name);
2695     if(l==0) {
2696         syntaxerror("Invalid point: \"%s\".", name);
2697     }
2698     l--;
2699     return *(SPOINT*)&mpoints.buffer[l];
2700 }
2701
2702 static int texture2(char*name, char*object, map_t*args, int errors)
2703 {
2704     SPOINT pos,size;
2705     char*xstr = map_lookup(args, "x");
2706     char*ystr = map_lookup(args, "y");
2707     char*widthstr = map_lookup(args, "width");
2708     char*heightstr = map_lookup(args, "height");
2709     char*scalestr = map_lookup(args, "scale");
2710     char*scalexstr = map_lookup(args, "scalex");
2711     char*scaleystr = map_lookup(args, "scaley");
2712     char*rotatestr = map_lookup(args, "rotate");
2713     char* shearstr = map_lookup(args, "shear");
2714     char* radiusstr = map_lookup(args, "r");
2715     float x=0,y=0;
2716     float scalex = 1.0, scaley = 1.0;
2717     float rotate=0, shear=0;
2718     float r = 0;
2719     if(!*xstr && !*ystr) {
2720         if(errors)
2721             syntaxerror("x and y must be set");
2722         return 0;
2723     }
2724     if(*scalestr && (*scalexstr || *scaleystr)) {
2725         syntaxerror("scale and scalex/scaley can't both be set");
2726         return 0;
2727     }
2728     if((*widthstr || *heightstr) && *radiusstr) {
2729         syntaxerror("width/height and radius can't both be set");
2730     }
2731     if(*radiusstr) {
2732         widthstr = radiusstr;
2733         heightstr = radiusstr;
2734     }
2735     if(!*xstr) xstr="0";
2736     if(!*ystr) ystr="0";
2737     if(!*rotatestr) rotatestr="0";
2738     if(!*shearstr) shearstr="0";
2739
2740     if(*scalestr) {
2741         scalex = scaley = parsePercent(scalestr);
2742     } else if(*scalexstr || *scaleystr) {
2743         if(scalexstr) scalex = parsePercent(scalexstr);
2744         if(scaleystr) scaley = parsePercent(scaleystr);
2745     } else if(*widthstr || *heightstr) {
2746         int width=0;
2747         int height=0;
2748         s_getBitmapSize(object, &width, &height);
2749         if(*widthstr)
2750             scalex = (float)parseTwip(widthstr)/(float)width;
2751         if(*heightstr)
2752             scaley = (float)parseTwip(heightstr)/(float)height;
2753     }
2754     x = parseTwip(xstr);
2755     y = parseTwip(ystr);
2756     rotate = parseFloat(rotatestr);
2757     shear = parseFloat(shearstr);
2758
2759     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2760
2761     return 0;
2762 }
2763
2764 static int c_texture(map_t*args)
2765 {
2766     char*name = lu(args, "instance");
2767     char*object = lu(args, "character");
2768     return texture2(name, object, args, 1);
2769 }
2770
2771 static int c_gradient(map_t*args)
2772 {
2773     char*name = lu(args, "name");
2774     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2775     int rotate = parseInt(lu(args, "rotate"));
2776
2777     readToken();
2778     if(type != RAWDATA)
2779         syntaxerror("colon (:) expected");
2780
2781     if(dictionary_lookup(&gradients, name))
2782         syntaxerror("gradient %s defined twice", name);
2783
2784     s_gradient(name, text, radial, rotate);
2785
2786     /* check whether we also have placement information,
2787        which would make this a positioned gradient.
2788        If there is placement information, texture2() will
2789        add a texture, which has priority over the gradient.
2790      */
2791     texture2(name, name, args, 0);
2792     return 0;
2793 }
2794
2795 static char* checkFiltername(map_t* args)
2796 {
2797     char* name = lu(args, "name");
2798     if (strchr(name, ','))
2799         syntaxerror("the comma (,) is used to separate filters in filterlists. Please do not use in filternames.");
2800     return name;
2801 }
2802
2803 static int c_blur(map_t*args)
2804 {
2805     char*name = checkFiltername(args);
2806     char*blurstr = lu(args, "blur");
2807     char*blurxstr = lu(args, "blurx");
2808     char*blurystr = lu(args, "blury");
2809     float blurx=1.0, blury=1.0;
2810     if(blurstr[0]) {
2811         blurx = parseFloat(blurstr);
2812         blury = parseFloat(blurstr);
2813     }
2814     if(blurxstr[0])
2815         blurx = parseFloat(blurxstr);
2816     if(blurystr[0])
2817         blury = parseFloat(blurystr);
2818     int passes = parseInt(lu(args, "passes"));
2819     s_blur(name, blurx, blury, passes);
2820     return 0;
2821 }
2822
2823 static int c_gradientglow(map_t*args)
2824 {
2825     char*name = checkFiltername(args);
2826     char*gradient = lu(args, "gradient");
2827     char*blurstr = lu(args, "blur");
2828     char*blurxstr = lu(args, "blurx");
2829     char*blurystr = lu(args, "blury");
2830     float blurx=1.0, blury=1.0;
2831     if(blurstr[0]) {
2832         blurx = parseFloat(blurstr);
2833         blury = parseFloat(blurstr);
2834     }
2835     if(blurxstr[0])
2836         blurx = parseFloat(blurxstr);
2837     if(blurystr[0])
2838         blury = parseFloat(blurystr);
2839
2840     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2841     float distance = parseFloat(lu(args, "distance"));
2842     float strength = parseFloat(lu(args, "strength"));
2843     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2844     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2845     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2846     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2847     int passes = parseInt(lu(args, "passes"));
2848
2849     s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2850     return 0;
2851 }
2852
2853 static int c_dropshadow(map_t*args)
2854 {
2855     char*name = checkFiltername(args);
2856     RGBA color = parseColor(lu(args, "color"));
2857     char*blurstr = lu(args, "blur");
2858     char*blurxstr = lu(args, "blurx");
2859     char*blurystr = lu(args, "blury");
2860     float blurx=1.0, blury=1.0;
2861     if(blurstr[0]) {
2862         blurx = parseFloat(blurstr);
2863         blury = parseFloat(blurstr);
2864     }
2865     if(blurxstr[0])
2866         blurx = parseFloat(blurxstr);
2867     if(blurystr[0])
2868         blury = parseFloat(blurystr);
2869
2870     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2871     float distance = parseFloat(lu(args, "distance"));
2872     float strength = parseFloat(lu(args, "strength"));
2873     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2874     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2875     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2876     int passes = parseInt(lu(args, "passes"));
2877
2878     s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
2879     return 0;
2880 }
2881
2882 static int c_bevel(map_t*args)
2883 {
2884     char*name = checkFiltername(args);
2885     RGBA shadow = parseColor(lu(args, "shadow"));
2886     RGBA highlight = parseColor(lu(args, "highlight"));
2887     char*blurstr = lu(args, "blur");
2888     char*blurxstr = lu(args, "blurx");
2889     char*blurystr = lu(args, "blury");
2890     float blurx=1.0, blury=1.0;
2891     if(blurstr[0]) {
2892         blurx = parseFloat(blurstr);
2893         blury = parseFloat(blurstr);
2894     }
2895     if(blurxstr[0])
2896         blurx = parseFloat(blurxstr);
2897     if(blurystr[0])
2898         blury = parseFloat(blurystr);
2899
2900     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2901     float distance = parseFloat(lu(args, "distance"));
2902     float strength = parseFloat(lu(args, "strength"));
2903     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2904     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2905     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2906     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2907     int passes = parseInt(lu(args, "passes"));
2908
2909     s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2910     return 0;
2911 }
2912
2913 static int c_point(map_t*args)
2914 {
2915     char*name = lu(args, "name");
2916     int pos;
2917     string_t s1;
2918     SPOINT p;
2919     if(!points_initialized) {
2920         dictionary_init(&points);
2921         mem_init(&mpoints);
2922         points_initialized = 1;
2923     }
2924     p.x = parseTwip(lu(args, "x"));
2925     p.y = parseTwip(lu(args, "y"));
2926     pos = mem_put(&mpoints, &p, sizeof(p));
2927     string_set(&s1, name);
2928     pos++;
2929     dictionary_put(&points, s1, (void*)pos);
2930     return 0;
2931 }
2932 static int c_play(map_t*args)
2933 {
2934     char*name = lu(args, "name");
2935     char*loop = lu(args, "loop");
2936     char*nomultiple = lu(args, "nomultiple");
2937     int nm = 0;
2938     if(!strcmp(nomultiple, "nomultiple"))
2939         nm = 1;
2940     else
2941         nm = parseInt(nomultiple);
2942
2943     if(s_playsound(name, parseInt(loop), nm, 0)) {
2944         return 0;
2945     } else if(s_swf3action(name, "play")) {
2946         return 0;
2947     }
2948     return 0;
2949 }
2950
2951 static int c_stop(map_t*args)
2952 {
2953     char*name = map_lookup(args, "name");
2954
2955     if(s_playsound(name, 0,0,1))
2956         return 0;
2957     else if(s_swf3action(name, "stop"))
2958         return 0;
2959     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
2960         return 0;
2961 }
2962
2963 static int c_nextframe(map_t*args)
2964 {
2965     char*name = lu(args, "name");
2966
2967     if(s_swf3action(name, "nextframe")) {
2968         return 0;
2969     }
2970     syntaxerror("I don't know anything about movie \"%s\"", name);
2971     return 0;
2972 }
2973
2974 static int c_previousframe(map_t*args)
2975 {
2976     char*name = lu(args, "name");
2977
2978     if(s_swf3action(name, "previousframe")) {
2979         return 0;
2980     }
2981     syntaxerror("I don't know anything about movie \"%s\"", name);
2982     return 0;
2983 }
2984
2985 static int c_movement(map_t*args, int type)
2986 {
2987     char*instance = lu(args, "name");
2988
2989     char* xstr="";
2990     char* ystr="";
2991     SRECT oldbbox;
2992     parameters_t p;
2993     U16 set = 0x0000;
2994
2995     xstr = lu(args, "x");
2996     ystr = lu(args, "y");
2997
2998     s_getParameters(instance, &p);
2999
3000     /* x,y position */
3001     if(xstr[0])
3002     {
3003         if(isRelative(xstr))
3004         {
3005             if(type == PT_PUT || type == PT_STARTCLIP)
3006                 syntaxerror("relative x values not allowed for initial put or startclip");
3007             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3008         }
3009         else
3010         {
3011             p.x = parseTwip(xstr);
3012         }
3013         set = set | SF_X;
3014      }
3015     if(ystr[0])
3016     {
3017         if(isRelative(ystr))
3018         {
3019             if(type == PT_PUT || type == PT_STARTCLIP)
3020                 syntaxerror("relative y values not allowed for initial put or startclip");
3021             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3022         }
3023         else
3024         {
3025             p.y = parseTwip(ystr);
3026         }
3027         set = set | SF_Y;
3028     }
3029
3030     if (change_sets_all)
3031         set = SF_ALL;
3032     p.set = set;
3033
3034     switch (type)
3035     {
3036         case PT_MOVE:
3037             {
3038                 char* interstr = lu(args, "interpolation");
3039                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3040                 if (!inter)
3041                     syntaxerror("unkown interpolation %s", interstr);
3042                 s_change(instance, p, inter);
3043             }
3044             break;
3045         case PT_SMOVE:
3046             {
3047                 char* interstr = lu(args, "interpolation");
3048                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3049                 if (!inter)
3050                     syntaxerror("unkown interpolation %s", interstr);
3051                 s_schange(instance, p, inter);
3052             }
3053             break;
3054         case PT_SWEEP:
3055             {
3056                 char* rstr = lu(args, "r");
3057                 int radius = parseTwip(rstr);
3058                 if (radius <= 0)
3059                         syntaxerror("sweep not possible: radius must be greater than 0.");
3060                 char* dirstr = lu(args, "dir");
3061                 int clockwise = parseDir(dirstr);
3062                 char* arcstr = lu(args, "arc");
3063                 int short_arc = parseArc(arcstr);
3064                 char* interstr = lu(args, "interpolation");
3065                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3066                 if (!inter)
3067                     syntaxerror("unkown interpolation %s", interstr);
3068                 s_sweep(instance, p, radius, clockwise, short_arc, inter);
3069             }
3070             break;
3071         }
3072     return 0;
3073 }
3074
3075 static int c_placement(map_t*args, int type)
3076 {
3077     char*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
3078     char*character = 0;
3079
3080     char* luminancestr = lu(args, "luminance");
3081     char* scalestr = lu(args, "scale");
3082     char* scalexstr = lu(args, "scalex");
3083     char* scaleystr = lu(args, "scaley");
3084     char* rotatestr = lu(args, "rotate");
3085     char* shearstr = lu(args, "shear");
3086     char* xstr="", *pivotstr="";
3087     char* ystr="", *anglestr="";
3088     char*above = lu(args, "above"); /*FIXME*/
3089     char*below = lu(args, "below");
3090     char* rstr = lu(args, "red");
3091     char* gstr = lu(args, "green");
3092     char* bstr = lu(args, "blue");
3093     char* astr = lu(args, "alpha");
3094     char* pinstr = lu(args, "pin");
3095     char* as = map_lookup(args, "as");
3096     char* blendmode = lu(args, "blend");
3097     char* filterstr = lu(args, "filter");
3098     U8 blend;
3099     MULADD r,g,b,a;
3100     float oldwidth;
3101     float oldheight;
3102     SRECT oldbbox;
3103     MULADD luminance;
3104     parameters_t p;
3105     U16 set = 0x0000;
3106
3107     if(type==9)
3108     { // (?) .rotate  or .arcchange
3109         pivotstr = lu(args, "pivot");
3110         anglestr = lu(args, "angle");
3111     }
3112     else
3113     {
3114         xstr = lu(args, "x");
3115         ystr = lu(args, "y");
3116     }
3117
3118     if(luminancestr[0])
3119         luminance = parseMulAdd(luminancestr);
3120     else
3121     {
3122         luminance.add = 0;
3123         luminance.mul = 256;
3124     }
3125
3126     if(scalestr[0])
3127     {
3128         if(scalexstr[0]||scaleystr[0])
3129             syntaxerror("scalex/scaley and scale cannot both be set");
3130         scalexstr = scaleystr = scalestr;
3131     }
3132
3133     if(type == PT_PUT || type == PT_STARTCLIP)  {
3134         // put or startclip
3135         character = lu(args, "character");
3136         parameters_clear(&p);
3137     } else if (type == PT_BUTTON) {
3138         character = lu(args, "name");
3139         parameters_clear(&p);
3140         // button's show
3141     } else {
3142         s_getParameters(instance, &p);
3143     }
3144
3145     /* x,y position */
3146     if(xstr[0])
3147     {
3148         if(isRelative(xstr))
3149         {
3150             if(type == PT_PUT || type == PT_STARTCLIP)
3151                 syntaxerror("relative x values not allowed for initial put or startclip");
3152             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3153         }
3154         else
3155         {
3156             p.x = parseTwip(xstr);
3157         }
3158         set = set | SF_X;
3159      }
3160     if(ystr[0])
3161     {
3162         if(isRelative(ystr))
3163         {
3164             if(type == PT_PUT || type == PT_STARTCLIP)
3165                 syntaxerror("relative y values not allowed for initial put or startclip");
3166             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3167         }
3168         else
3169         {
3170             p.y = parseTwip(ystr);
3171         }
3172         set = set | SF_Y;
3173         }
3174
3175     /* scale, scalex, scaley */
3176     if(character)
3177         oldbbox = s_getCharBBox(character);
3178     else
3179         oldbbox = s_getInstanceBBox(instance);
3180     oldwidth = oldbbox.xmax - oldbbox.xmin;
3181     oldheight = oldbbox.ymax - oldbbox.ymin;
3182     if(scalexstr[0])
3183     {
3184         if(oldwidth==0)
3185             p.scalex = 1.0;
3186         else
3187             if(scalexstr[0])
3188                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
3189         set = set | SF_SCALEX;
3190     }
3191    if(scaleystr[0])
3192    {
3193         if(oldheight==0)
3194             p.scaley = 1.0;
3195         else
3196             if(scaleystr[0])
3197                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
3198         set = set | SF_SCALEY;
3199    }
3200
3201     /* rotation */
3202     if(rotatestr[0])
3203     {
3204         if(isRelative(rotatestr))
3205             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
3206         else
3207             p.rotate = parseFloat(rotatestr);
3208         set = set | SF_ROTATE;
3209         }
3210
3211     /* shearing */
3212     if(shearstr[0])
3213     {
3214         if(isRelative(shearstr))
3215             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
3216         else
3217             p.shear = parseFloat(shearstr);
3218         set = set | SF_SHEAR;
3219     }
3220
3221     if(pivotstr[0])
3222     {
3223         if(isPoint(pivotstr))
3224             p.pivot = parsePoint(pivotstr);
3225         else
3226             p.pivot = getPoint(oldbbox, pivotstr);
3227         set = set | SF_PIVOT;
3228     }
3229
3230     if(pinstr[0])
3231     {
3232         if(isPoint(pinstr))
3233             p.pin = parsePoint(pinstr);
3234         else
3235             p.pin = getPoint(oldbbox, pinstr);
3236         set = set | SF_PIN;
3237     }
3238
3239     /* color transform */
3240
3241     if(rstr[0] || luminancestr[0])
3242     {
3243         MULADD r;
3244         if(rstr[0])
3245             r = parseMulAdd(rstr);
3246         else
3247         {
3248             r.add = p.cxform.r0;
3249             r.mul = p.cxform.r1;
3250         }
3251         r = mergeMulAdd(r, luminance);
3252         p.cxform.r0 = r.mul;
3253         p.cxform.r1 = r.add;
3254         set = set | SF_CX_R;
3255         }
3256     if(gstr[0] || luminancestr[0])
3257     {
3258         MULADD g;
3259         if(gstr[0])
3260             g = parseMulAdd(gstr);
3261         else
3262         {
3263             g.add = p.cxform.g0;
3264             g.mul = p.cxform.g1;
3265         }
3266         g = mergeMulAdd(g, luminance);
3267         p.cxform.g0 = g.mul;
3268         p.cxform.g1 = g.add;
3269         set = set | SF_CX_G;
3270     }
3271     if(bstr[0] || luminancestr[0])
3272     {
3273         MULADD b;
3274         if(bstr[0])
3275             b = parseMulAdd(bstr);
3276         else
3277         {
3278             b.add = p.cxform.b0;
3279             b.mul = p.cxform.b1;
3280         }
3281         b = mergeMulAdd(b, luminance);
3282         p.cxform.b0 = b.mul;
3283         p.cxform.b1 = b.add;
3284         set = set | SF_CX_B;
3285     }
3286     if(astr[0])
3287     {
3288         MULADD a = parseMulAdd(astr);
3289         p.cxform.a0 = a.mul;
3290         p.cxform.a1 = a.add;
3291         set = set | SF_CX_A;
3292     }
3293
3294     if(blendmode[0])
3295     {
3296         int t;
3297         blend = 255;
3298         for(t = 0; blendModeNames[t]; t++)
3299         {
3300             if(!strcmp(blendModeNames[t], blendmode))
3301             {
3302                 blend = t;
3303                 break;
3304             }
3305         }
3306         if(blend == 255)
3307         {
3308             syntaxerror("unknown blend mode: '%s'", blendmode);
3309         }
3310         p.blendmode = blend;
3311         set = set | SF_BLEND;
3312         }
3313
3314     if(filterstr[0])
3315     {
3316         p.filters = parseFilters(filterstr);
3317         set = set | SF_FILTER;
3318     }
3319
3320     if (type == PT_CHANGE && set & (SF_X | SF_Y))
3321         warning("As of version 0.8.2 using the .change command to modify an \
3322 object's position on the stage is considered deprecated. Future \
3323 versions may consider x and y parameters for the .change command \
3324 to be illegal; please use the .move command.");
3325
3326     if (change_sets_all)
3327         set = SF_ALL;
3328     p.set = set;
3329
3330     switch (type)
3331     {
3332         case PT_PUT:
3333             s_put(instance, character, p);
3334             break;
3335         case PT_CHANGE:
3336             {
3337                 char* interstr = lu(args, "interpolation");
3338                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3339                 if (!inter)
3340                     syntaxerror("unkown interpolation %s", interstr);
3341                 s_change(instance, p, inter);
3342             }
3343             break;
3344         case PT_SCHANGE:
3345             {
3346                 char* interstr = lu(args, "interpolation");
3347                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3348                 if (!inter)
3349                     syntaxerror("unkown interpolation %s", interstr);
3350                 s_schange(instance, p, inter);
3351             }
3352             break;
3353         case PT_JUMP:
3354             s_jump(instance, p);
3355             break;
3356         case PT_STARTCLIP:
3357             s_startclip(instance, character, p);
3358             break;
3359         case PT_BUTTON:
3360             if(as && as[0])
3361                 s_buttonput(character, as, p);
3362             else
3363                 s_buttonput(character, "shape", p);
3364             break;
3365 //        default:
3366         }
3367     return 0;
3368 }
3369 static int c_put(map_t*args)
3370 {
3371     c_placement(args, PT_PUT);
3372     return 0;
3373 }
3374 static int c_change(map_t*args)
3375 {
3376     if (currentframe == 0)
3377         warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
3378     c_placement(args, PT_CHANGE);
3379     return 0;
3380 }
3381 static int c_schange(map_t*args)
3382 {
3383     c_placement(args, PT_SCHANGE);
3384     return 0;
3385 }
3386 static int c_move(map_t* args)
3387 {
3388     c_movement(args, PT_MOVE);
3389     return 0;
3390 }
3391 static int c_smove(map_t* args)
3392 {
3393     c_movement(args, PT_SMOVE);
3394     return 0;
3395 }
3396 static int c_sweep(map_t* args)
3397 {
3398     c_movement(args, PT_SWEEP);
3399     return 0;
3400 }
3401 static int c_arcchange(map_t*args)
3402 {
3403     c_placement(args, 0);
3404     return 0;
3405 }
3406 static int c_jump(map_t*args)
3407 {
3408     c_placement(args, PT_JUMP);
3409     return 0;
3410 }
3411 static int c_startclip(map_t*args)
3412 {
3413     c_placement(args, PT_STARTCLIP);
3414     return 0;
3415 }
3416 static int c_show(map_t*args)
3417 {
3418     c_placement(args, PT_BUTTON);
3419     return 0;
3420 }
3421 static int c_toggle(map_t* args)
3422 {
3423     char*instance = lu(args, "name");
3424     U16 flagsOn = 0x0000, flagsOff = 0xffff;
3425     char* alignstr = lu(args, "fixed_alignment");
3426     if (!strcmp(alignstr, "on"))
3427         flagsOn += IF_FIXED_ALIGNMENT;
3428     else
3429         if (!strcmp(alignstr, "off"))
3430             flagsOff -= IF_FIXED_ALIGNMENT;
3431         else
3432             syntaxerror("values for toggle must be \"on\" or \"off\". %s is not legal.", alignstr);
3433     s_toggle(instance, flagsOn, flagsOff);
3434     return 0;
3435 }
3436 static int c_del(map_t*args)
3437 {
3438     char*instance = lu(args, "name");
3439     s_delinstance(instance);
3440     return 0;
3441 }
3442 static int c_end(map_t*args)
3443 {
3444     s_end();
3445     return 0;
3446 }
3447 static int c_sprite(map_t*args)
3448 {
3449     char* name = lu(args, "name");
3450     s_sprite(name);
3451     return 0;
3452 }
3453 static int c_frame(map_t*args)
3454 {
3455     char*framestr = lu(args, "n");
3456     char*cutstr = lu(args, "cut");
3457
3458     char*name = lu(args, "name");
3459     char*anchor = lu(args, "anchor");
3460     char buf[40];
3461
3462     if(!strcmp(anchor, "anchor") && !*name)
3463         name = framestr;
3464
3465     int frame;
3466     int cut = 0;
3467     if(strcmp(cutstr, "no"))
3468         cut = 1;
3469     if(isRelative(framestr)) {
3470         frame = s_getframe();
3471         if(getSign(framestr)<0)
3472             syntaxerror("relative frame expressions must be positive");
3473         frame += parseInt(getOffset(framestr));
3474     }
3475     else {
3476         frame = parseInt(framestr);
3477         if(s_getframe() >= frame
3478                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
3479             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
3480     }
3481     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
3482     return 0;
3483 }
3484 static int c_primitive(map_t*args)
3485 {
3486     char*name = lu(args, "name");
3487     char*command = lu(args, "commandname");
3488     int width=0, height=0, r=0;
3489     int linewidth = parseTwip(lu(args, "line"));
3490     char*colorstr = lu(args, "color");
3491     RGBA color = parseColor(colorstr);
3492     char*fillstr = lu(args, "fill");
3493     int dofill = 1;
3494     int type=0;
3495     char* font;
3496     char* text;
3497     char* outline=0;
3498     RGBA fill;
3499     if(!strcmp(command, "circle"))
3500         type = 1;
3501     else if(!strcmp(command, "filled"))
3502         type = 2;
3503
3504     if(type==0) {
3505         width = parseTwip(lu(args, "width"));
3506         height = parseTwip(lu(args, "height"));
3507     } else if (type==1) {
3508         r = parseTwip(lu(args, "r"));
3509     } else if (type==2) {
3510         outline = lu(args, "outline");
3511     }
3512
3513     if(!strcmp(fillstr, "fill"))
3514         fillstr = colorstr;
3515     if(!strcmp(fillstr, "none"))
3516         fillstr = 0;
3517     if(width<0 || height<0 || linewidth<0 || r<0)
3518         syntaxerror("values width, height, line, r must be positive");
3519
3520     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
3521     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
3522     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
3523     return 0;
3524 }
3525
3526 static int c_textshape(map_t*args)
3527 {
3528     char*name = lu(args, "name");
3529     char*text = lu(args, "text");
3530     char*font = lu(args, "font");
3531     float size = parsePxOrPercent(font, lu(args, "size"));
3532
3533     s_textshape(name, font, size, text);
3534     return 0;
3535 }
3536
3537 static int c_swf(map_t*args)
3538 {
3539     char*name = lu(args, "name");
3540     char*filename = lu(args, "filename");
3541     char*command = lu(args, "commandname");
3542     if(!strcmp(command, "shape"))
3543         warning("Please use .swf instead of .shape");
3544     s_includeswf(name, filename);
3545     return 0;
3546 }
3547
3548 static int c_font(map_t*args)
3549 {
3550     char*name = lu(args, "name");
3551     char*filename = lu(args, "filename");
3552     s_font(name, filename);
3553     return 0;
3554 }
3555
3556 static int c_sound(map_t*args)
3557 {
3558     char*name = lu(args, "name");
3559     char*filename = lu(args, "filename");
3560     s_sound(name, filename);
3561     return 0;
3562 }
3563
3564 static int c_text(map_t*args)
3565 {
3566     char*name = lu(args, "name");
3567     char*text = lu(args, "text");
3568     char*font = lu(args, "font");
3569     float size = parsePxOrPercent(font, lu(args, "size"));
3570     RGBA color = parseColor(lu(args, "color"));
3571     s_text(name, font, text, (int)(size*100), color);
3572     return 0;
3573 }
3574
3575 static int c_soundtrack(map_t*args)
3576 {
3577     return 0;
3578 }
3579
3580 static int c_quicktime(map_t*args)
3581 {
3582     char*name = lu(args, "name");
3583     char*url = lu(args, "url");
3584     s_quicktime(name, url);
3585     return 0;
3586 }
3587
3588 static int c_image(map_t*args)
3589 {
3590     char*command = lu(args, "commandname");
3591     char*name = lu(args, "name");
3592     char*filename = lu(args, "filename");
3593     if(!strcmp(command,"jpeg")) {
3594         int quality = (int)(parsePercent(lu(args, "quality"))*100);
3595         s_image(name, "jpeg", filename, quality);
3596     } else {
3597         s_image(name, "png", filename, 0);
3598     }
3599     return 0;
3600 }
3601
3602 static int c_outline(map_t*args)
3603 {
3604     char*name = lu(args, "name");
3605     char*format = lu(args, "format");
3606
3607     readToken();
3608     if(type != RAWDATA)
3609         syntaxerror("colon (:) expected");
3610
3611     s_outline(name, format, text);
3612     return 0;
3613 }
3614
3615 int fakechar(map_t*args)
3616 {
3617     char*name = lu(args, "name");
3618     s_box(name, 0, 0, black, 20, 0);
3619     return 0;
3620 }
3621
3622 static int c_egon(map_t*args) {return fakechar(args);}
3623 static int c_button(map_t*args) {
3624     char*name = lu(args, "name");
3625     s_button(name);
3626     return 0;
3627 }
3628 static int current_button_flags = 0;
3629 static int c_on_press(map_t*args)
3630 {
3631     char*position = lu(args, "position");
3632     char*action = "";
3633     if(!strcmp(position, "inside")) {
3634         current_button_flags |= BC_OVERUP_OVERDOWN;
3635     } else if(!strcmp(position, "outside")) {
3636         //current_button_flags |= BC_IDLE_OUTDOWN;
3637         syntaxerror("IDLE_OVERDOWN not supported by SWF");
3638     } else if(!strcmp(position, "anywhere")) {
3639         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
3640     }
3641     readToken();
3642     if(type == RAWDATA) {
3643         action = text;
3644         s_buttonaction(current_button_flags, action);
3645         current_button_flags = 0;
3646     }
3647     else
3648         pushBack();
3649     return 0;
3650 }
3651 static int c_on_release(map_t*args)
3652 {
3653     char*position = lu(args, "position");
3654     char*action = "";
3655     if(!strcmp(position, "inside")) {
3656         current_button_flags |= BC_OVERDOWN_OVERUP;
3657     } else if(!strcmp(position, "outside")) {
3658         current_button_flags |= BC_OUTDOWN_IDLE;
3659     } else if(!strcmp(position, "anywhere")) {
3660         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
3661     }
3662     readToken();
3663     if(type == RAWDATA) {
3664         action = text;
3665         s_buttonaction(current_button_flags, action);
3666         current_button_flags = 0;
3667     }
3668     else
3669         pushBack();
3670     return 0;
3671 }
3672 static int c_on_move_in(map_t*args)
3673 {
3674     char*position = lu(args, "state");
3675     char*action = "";
3676     if(!strcmp(position, "pressed")) {
3677         current_button_flags |= BC_OUTDOWN_OVERDOWN;
3678     } else if(!strcmp(position, "not_pressed")) {
3679         current_button_flags |= BC_IDLE_OVERUP;
3680     } else if(!strcmp(position, "any")) {
3681         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|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_move_out(map_t*args)
3694 {
3695     char*position = lu(args, "state");
3696     char*action = "";
3697     if(!strcmp(position, "pressed")) {
3698         current_button_flags |= BC_OVERDOWN_OUTDOWN;
3699     } else if(!strcmp(position, "not_pressed")) {
3700         current_button_flags |= BC_OVERUP_IDLE;
3701     } else if(!strcmp(position, "any")) {
3702         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_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_key(map_t*args)
3715 {
3716     char*key = lu(args, "key");
3717     char*action = "";
3718     if(strlen(key)==1) {
3719         /* ascii */
3720         if(key[0]>=32) {
3721             current_button_flags |= 0x4000 + (key[0]*0x200);
3722         } else {
3723             syntaxerror("invalid character: %c"+key[0]);
3724             return 1;
3725         }
3726     } else {
3727         /* TODO:
3728            <ctrl-x> = 0x200*(x-'a')
3729            esc = = 0x3600
3730            space = = 0x4000;
3731         */
3732         syntaxerror("invalid key: %s",key);
3733     }
3734     readToken();
3735     if(type == RAWDATA) {
3736         action = text;
3737         s_buttonaction(current_button_flags, action);
3738         current_button_flags = 0;
3739     }
3740     else
3741         pushBack();
3742     return 0;
3743 }
3744
3745 static int c_edittext(map_t*args)
3746 {
3747  //"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"},
3748     char*name = lu(args, "name");
3749     char*font = lu(args, "font");
3750     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
3751     int width = parseTwip(lu(args, "width"));
3752     int height = parseTwip(lu(args, "height"));
3753     char*text  = lu(args, "text");
3754     RGBA color = parseColor(lu(args, "color"));
3755     int maxlength = parseInt(lu(args, "maxlength"));
3756     char*variable = lu(args, "variable");
3757     char*passwordstr = lu(args, "password");
3758     char*wordwrapstr = lu(args, "wordwrap");
3759     char*multilinestr = lu(args, "multiline");
3760     char*htmlstr = lu(args, "html");
3761     char*noselectstr = lu(args, "noselect");
3762     char*readonlystr = lu(args, "readonly");
3763     char*borderstr = lu(args, "border");
3764     char*autosizestr = lu(args, "autosize");
3765     char*alignstr = lu(args, "align");
3766     int align = -1;
3767
3768     int flags = 0;
3769     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
3770     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
3771     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
3772     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
3773     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
3774     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
3775     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
3776     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
3777     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
3778     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
3779     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
3780     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
3781     else syntaxerror("Unknown alignment: %s", alignstr);
3782
3783     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
3784     return 0;
3785 }
3786
3787 static int c_morphshape(map_t*args) {return fakechar(args);}
3788 static int c_movie(map_t*args) {return fakechar(args);}
3789
3790 static char* readfile(const char*filename)
3791 {
3792     FILE*fi = fopen(filename, "rb");
3793     int l;
3794     char*text;
3795     if(!fi)
3796         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
3797     fseek(fi, 0, SEEK_END);
3798     l = ftell(fi);
3799     fseek(fi, 0, SEEK_SET);
3800     text = rfx_alloc(l+1);
3801     fread(text, l, 1, fi);
3802     text[l]=0;
3803     fclose(fi);
3804     return text;
3805 }
3806
3807 static int c_action(map_t*args)
3808 {
3809     char* filename  = map_lookup(args, "filename");
3810     if(!filename ||!*filename) {
3811         readToken();
3812         if(type != RAWDATA) {
3813             syntaxerror("colon (:) expected");
3814         }
3815         s_action(text);
3816     } else {
3817         s_action(readfile(filename));
3818     }
3819
3820     return 0;
3821 }
3822
3823 static int c_initaction(map_t*args)
3824 {
3825     char* character = lu(args, "name");
3826     char* filename  = map_lookup(args, "filename");
3827     if(!filename ||!*filename) {
3828         readToken();
3829         if(type != RAWDATA) {
3830             syntaxerror("colon (:) expected");
3831         }
3832         s_initaction(character, text);
3833     } else {
3834         s_initaction(character, readfile(filename));
3835     }
3836
3837     return 0;
3838 }
3839
3840 static struct {
3841     char*command;
3842     command_func_t* func;
3843     char*arguments;
3844 } arguments[] =
3845 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no"},
3846  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
3847  // "import" type stuff
3848  {"swf", c_swf, "name filename"},
3849  {"shape", c_swf, "name filename"},
3850  {"jpeg", c_image, "name filename quality=80%"},
3851  {"png", c_image, "name filename"},
3852  {"movie", c_movie, "name filename"},
3853  {"sound", c_sound, "name filename"},
3854  {"font", c_font, "name filename glyphs="},
3855  {"soundtrack", c_soundtrack, "filename"},
3856  {"quicktime", c_quicktime, "url"},
3857
3858     // generators of primitives
3859
3860  {"point", c_point, "name x=0 y=0"},
3861  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
3862  {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5 damping=2 slope=0"},
3863  {"outline", c_outline, "name format=simple"},
3864  {"textshape", c_textshape, "name font size=100% text"},
3865
3866     // filters
3867  {"blur", c_blur, "name blur= blurx= blury= passes=1"},
3868  {"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"},
3869  {"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"},
3870  {"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"},
3871
3872     // character generators
3873  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
3874  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
3875  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
3876
3877  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
3878  {"text", c_text, "name text font size=100% color=white"},
3879  {"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="},
3880  {"morphshape", c_morphshape, "name start end"},
3881  {"button", c_button, "name"},
3882     {"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="},
3883     {"on_press", c_on_press, "position=inside"},
3884     {"on_release", c_on_release, "position=anywhere"},
3885     {"on_move_in", c_on_move_in, "state=not_pressed"},
3886     {"on_move_out", c_on_move_out, "state=not_pressed"},
3887     {"on_key", c_on_key, "key=any"},
3888
3889     // control tags
3890  {"play", c_play, "name loop=0 @nomultiple=0"},
3891  {"stop", c_stop, "name= "},
3892  {"nextframe", c_nextframe, "name"},
3893  {"previousframe", c_previousframe, "name"},
3894
3895     // object placement tags
3896  {"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="},
3897  {"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="},
3898  {"move", c_move,       "name x= y= interpolation=linear"},
3899  {"smove", c_smove,     "name x= y= interpolation=linear"},
3900  {"sweep", c_sweep,     "name x= y= r= dir=counterclockwise arc=short interpolation=linear"},
3901  {"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"},
3902  //{"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3903  {"schange", c_schange, "name red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
3904  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3905  {"del", c_del, "name"},
3906     // virtual object placement
3907  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
3908     // switching
3909  {"toggle", c_toggle, "name fixed_alignment="},
3910
3911     // commands which start a block
3912 //startclip (see above)
3913  {"sprite", c_sprite, "name"},
3914  {"action", c_action, "filename="},
3915  {"initaction", c_initaction, "name filename="},
3916
3917  {"end", c_end, ""}
3918 };
3919
3920
3921 static map_t parseArguments(char*command, char*pattern)
3922 {
3923     char*x;
3924     char*d,*e;
3925
3926     string_t name[64];
3927     string_t value[64];
3928     int set[64];
3929     int isboolean[64];
3930     int pos;
3931     int len;
3932     int t;
3933     string_t t1,t2;
3934     map_t result;
3935     map_init(&result);
3936
3937     string_set(&t1, "commandname");
3938     string_set(&t2, command);
3939     map_put(&result, t1, t2);
3940
3941     if(!pattern || !*pattern)
3942         return result;
3943
3944     x = pattern;
3945
3946     pos = 0;
3947
3948     if(!strncmp("<i> ", x, 3)) {
3949         readToken();
3950         if(type == COMMAND || type == RAWDATA) {
3951             pushBack();
3952             syntaxerror("character name expected");
3953         }
3954         name[pos].str = "instance";
3955         name[pos].len = 8;
3956         value[pos].str = text;
3957         value[pos].len = strlen(text);
3958         set[pos] = 1;
3959         pos++;
3960
3961         if(type == ASSIGNMENT)
3962             readToken();
3963
3964         name[pos].str = "character";
3965         name[pos].len = 9;
3966         value[pos].str = text;
3967         value[pos].len = strlen(text);
3968         set[pos] = 1;
3969         pos++;
3970
3971         x+=4;
3972     }
3973
3974     while(*x) {
3975         isboolean[pos] = (x[0] =='@');
3976         if(isboolean[pos])
3977             x++;
3978
3979         d = strchr(x, ' ');
3980         e = strchr(x, '=');
3981         if(!d)
3982             d=&x[strlen(x)];
3983         set[pos] = 0;
3984
3985         if(!e || d<e) {
3986             // no default
3987             name[pos].str = x;
3988             name[pos].len = d-x;
3989             value[pos].str = 0;
3990             value[pos].len = 0;
3991         } else {
3992             name[pos].str = x;
3993             name[pos].len = e-x;
3994             value[pos].str = e+1;
3995             value[pos].len = d-e-1;
3996         }
3997         pos++;
3998         if(!*d) break;
3999         x=d+1;
4000     }
4001     len = pos;
4002
4003 /*    for(t=0;t<len;t++) {
4004         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
4005                 isboolean[t]?"(boolean)":"");
4006     }*/
4007
4008     while(1) {
4009         readToken();
4010         if(type == RAWDATA || type == COMMAND) {
4011             pushBack();
4012             break;
4013         }
4014
4015         // first, search for boolean arguments
4016         for(pos=0;pos<len;pos++)
4017         {
4018             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
4019                 set[pos] = 1;
4020                 if(type == ASSIGNMENT)
4021                     readToken();
4022                 value[pos].str = text;
4023                 value[pos].len = strlen(text);
4024                 /*printf("setting boolean parameter %s (to %s)\n",
4025                         strdup_n(name[pos], namelen[pos]),
4026                         strdup_n(value[pos], valuelen[pos]));*/
4027                 break;
4028             }
4029         }
4030
4031         // second, search for normal arguments
4032         if(pos==len)
4033         for(pos=0;pos<len;pos++)
4034         {
4035             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
4036                (type != ASSIGNMENT && !set[pos])) {
4037                 if(set[pos]) {
4038                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
4039                 }
4040                 if(type == ASSIGNMENT)
4041                     readToken();
4042                 set[pos] = 1;
4043                 value[pos].str = text;
4044                 value[pos].len = strlen(text);
4045 #if 0//def DEBUG
4046                 printf("setting parameter %s (to %s)\n",
4047                         strdup_n(name[pos].str, name[pos].len),
4048                         strdup_n(value[pos].str, value[pos].len));
4049 #endif
4050                 break;
4051             }
4052         }
4053         if(pos==len) {
4054             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
4055         }
4056     }
4057 #if 0//def DEBUG
4058     for(t=0;t<len;t++) {
4059         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
4060     }
4061 #endif
4062     for(t=0;t<len;t++) {
4063         if(value[t].str && value[t].str[0] == '*') {
4064             //relative default- take value from some other parameter
4065             int s;
4066             for(s=0;s<len;s++) {
4067                 if(value[s].len == value[t].len-1 &&
4068                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
4069                     value[t].str = value[s].str;
4070             }
4071         }
4072         if(value[t].str == 0) {
4073             pushBack();
4074             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
4075         }
4076     }
4077
4078     /* ok, now construct the dictionary from the parameters */
4079
4080     for(t=0;t<len;t++)
4081     {
4082         map_put(&result, name[t], value[t]);
4083     }
4084     return result;
4085 }
4086 static void parseArgumentsForCommand(char*command)
4087 {
4088     int t;
4089     map_t args;
4090     int nr = -1;
4091     msg("<verbose> parse Command: %s (line %d)", command, line);
4092
4093     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
4094         if(!strcmp(arguments[t].command, command)) {
4095
4096             /* ugly hack- will be removed soon (once documentation and .sc generating
4097                utilities have been changed) */
4098             if(!strcmp(command, "swf") && !stackpos) {
4099                 warning("Please use .flash instead of .swf- this will be mandatory soon");
4100                 command = "flash";
4101                 t = 0;
4102             }
4103
4104             args = parseArguments(command, arguments[t].arguments);
4105             nr = t;
4106             break;
4107         }
4108     }
4109     if(nr<0)
4110         syntaxerror("command %s not known", command);
4111
4112     // catch missing .flash directives at the beginning of a file
4113     if(strcmp(command, "flash") && !stackpos)
4114     {
4115         syntaxerror("No movie defined- use .flash first");
4116     }
4117
4118 #ifdef DEBUG
4119     printf(".%s\n", command);fflush(stdout);
4120     map_dump(&args, stdout, "\t");fflush(stdout);
4121 #endif
4122
4123     (*arguments[nr].func)(&args);
4124
4125     /*if(!strcmp(command, "button") ||
4126        !strcmp(command, "action")) {
4127         while(1) {
4128             readToken();
4129             if(type == COMMAND) {
4130                 if(!strcmp(text, "end"))
4131                     break;
4132                 else {
4133                     pushBack();
4134                     break;
4135                 }
4136             }
4137         }
4138     }*/
4139
4140     map_clear(&args);
4141     return;
4142 }
4143
4144
4145 /* for now only intended to find what glyphs of each font are to be included in the swf file.
4146  * Therefore some knowledge about the command is assumed i.e. it is font/text-related
4147  * No syntax checking is done */
4148 static void analyseArgumentsForCommand(char*command)
4149 {
4150     int t;
4151     map_t args;
4152     char* fontfile;
4153     int nr = -1;
4154     msg("<verbose> analyse Command: %s (line %d)", command, line);
4155
4156     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++)
4157     {
4158         if(!strcmp(arguments[t].command, command))
4159         {
4160             args = parseArguments(command, arguments[t].arguments);
4161             nr = t;
4162             break;
4163         }
4164     }
4165 #ifdef DEBUG
4166     printf(".%s\n", command);fflush(stdout);
4167     map_dump(&args, stdout, "\t");fflush(stdout);
4168 #endif
4169     char* name = lu(&args, "name");
4170     if (!strcmp(command, "font"))
4171     {
4172         if(dictionary_lookup(&fonts, name))
4173             syntaxerror("font %s defined twice", name);
4174
4175         SWFFONT* font;
4176         fontfile = lu(&args, "filename");
4177         font = swf_LoadFont(fontfile);
4178         if(font == 0) {
4179             warning("Couldn't open font file \"%s\"", fontfile);
4180             font = (SWFFONT*)malloc(sizeof(SWFFONT));
4181             memset(font, 0, sizeof(SWFFONT));
4182         }
4183         swf_FontUseUTF8(font, lu(&args, "glyphs"));
4184         swf_FontPrepareForEditText(font);
4185         dictionary_put2(&fonts, name, font);
4186     }
4187     else
4188     {
4189         SWFFONT* font = dictionary_lookup(&fonts, lu(&args, "font"));
4190         if (!font)
4191             syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
4192         else
4193             if (!strcmp(command, "edittext"))
4194                 swf_FontUseAll(font);
4195             else
4196                 swf_FontUseUTF8(font, lu(&args, "text"));
4197     }
4198     map_clear(&args);
4199     return;
4200 }
4201
4202 void skipParameters()
4203 {
4204         do
4205                 readToken();
4206         while (type != COMMAND);
4207         pushBack();
4208 }
4209
4210 void findFontUsage()
4211 {
4212     char* fontRelated = "font;text;textshape;edittext;";
4213     while(!noMoreTokens())
4214     {
4215         readToken();
4216         if(type != COMMAND)
4217             syntaxerror("command expected");
4218         if (strstr(fontRelated, text))
4219             analyseArgumentsForCommand(text);
4220         else
4221             if(strcmp(text, "end"))
4222                 skipParameters();
4223     }
4224 }
4225
4226 void firstPass()
4227 {
4228     pos = 0;
4229     id = 0;
4230     dictionary_init(&fonts);
4231     cleanUp = &freeFontDictionary;
4232     findFontUsage();
4233 }
4234
4235 int main (int argc,char ** argv)
4236 {
4237     int t;
4238     processargs(argc, argv);
4239     initLog(0,-1,0,0,-1,verbose);
4240
4241     if(!filename) {
4242         args_callback_usage(argv[0]);
4243         exit(1);
4244     }
4245
4246     file = generateTokens(filename);
4247     if(!file) {
4248         fprintf(stderr, "parser returned error.\n");
4249         return 1;
4250     }
4251     firstPass();
4252     pos=0;
4253     t=0;
4254
4255     while(!noMoreTokens()) {
4256         readToken();
4257         if(type != COMMAND)
4258             syntaxerror("command expected");
4259         parseArgumentsForCommand(text);
4260     }
4261
4262     s_close();
4263     freeTokens(file);
4264
4265     return 0;
4266 }
4267