added Huub's fix for a bug in the parseTwip function
[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
1043         {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
1044
1045     close(fi);
1046
1047     freeDictionaries();
1048
1049     swf_FreeTags(swf);
1050     free(swf);
1051     free(filename);
1052 }
1053
1054 void s_close()
1055 {
1056     if(stackpos) {
1057         if(stack[stackpos-1].type == 0)
1058             syntaxerror("End of file encountered in .flash block");
1059         if(stack[stackpos-1].type == 1)
1060             syntaxerror("End of file encountered in .sprite block");
1061         if(stack[stackpos-1].type == 2)
1062             syntaxerror("End of file encountered in .clip block");
1063     }
1064 }
1065
1066 int s_getframe()
1067 {
1068     return currentframe+1;
1069 }
1070
1071 void s_frame(int nr, int cut, char*name, char anchor)
1072 {
1073     int t;
1074     TAG*now = tag;
1075
1076     if(nr<1)
1077         syntaxerror("Illegal frame number");
1078     nr--; // internally, frame 1 is frame 0
1079
1080     for(t=currentframe;t<nr;t++) {
1081         tag = swf_InsertTag(tag, ST_SHOWFRAME);
1082         if(t==nr-1 && name && *name) {
1083             tag = swf_InsertTag(tag, ST_FRAMELABEL);
1084             swf_SetString(tag, name);
1085             if(anchor)
1086                 swf_SetU8(tag, 1); //make this an anchor
1087         }
1088     }
1089     if(nr == 0 && currentframe == 0 && name && *name) {
1090         tag = swf_InsertTag(tag, ST_FRAMELABEL);
1091         swf_SetString(tag, name);
1092         if(anchor)
1093             swf_SetU8(tag, 1); //make this an anchor
1094     }
1095
1096     if(cut) {
1097         if(now == tag) {
1098             syntaxerror("Can't cut, frame empty");
1099         }
1100         stack[stackpos].cut = tag;
1101     }
1102
1103     currentframe = nr;
1104 }
1105
1106 int parseColor2(char*str, RGBA*color);
1107
1108 int addFillStyle(SHAPE*s, SRECT*r, char*name)
1109 {
1110     RGBA color;
1111     character_t*image;
1112     gradient_t*gradient;
1113     texture_t*texture;
1114     if(name[0] == '#') {
1115         parseColor2(name, &color);
1116         return swf_ShapeAddSolidFillStyle(s, &color);
1117     } else if ((texture = dictionary_lookup(&textures, name))) {
1118         return swf_ShapeAddFillStyle2(s, &texture->fs);
1119     } else if((image = dictionary_lookup(&images, name))) {
1120         MATRIX m;
1121         swf_GetMatrix(0, &m);
1122         m.sx = 65536.0*20.0*(r->xmax - r->xmin)/image->size.xmax;
1123         m.sy = 65536.0*20.0*(r->ymax - r->ymin)/image->size.ymax;
1124         m.tx = r->xmin;
1125         m.ty = r->ymin;
1126         return swf_ShapeAddBitmapFillStyle(s, &m, image->id, 0);
1127     }  else if ((gradient = dictionary_lookup(&gradients, name))) {
1128         SRECT r2;
1129         MATRIX rot,m;
1130         double ccos,csin;
1131         swf_GetMatrix(0, &rot);
1132         ccos = cos(-gradient->rotate*2*M_PI/360);
1133         csin = sin(-gradient->rotate*2*M_PI/360);
1134         rot.sx =  ccos*65536;
1135         rot.r1 = -csin*65536;
1136         rot.r0 =  csin*65536;
1137         rot.sy =  ccos*65536;
1138         r2 = swf_TurnRect(*r, &rot);
1139         swf_GetMatrix(0, &m);
1140         m.sx =  (r2.xmax - r2.xmin)*2*ccos;
1141         m.r1 = -(r2.xmax - r2.xmin)*2*csin;
1142         m.r0 =  (r2.ymax - r2.ymin)*2*csin;
1143         m.sy =  (r2.ymax - r2.ymin)*2*ccos;
1144         m.tx = r->xmin + (r->xmax - r->xmin)/2;
1145         m.ty = r->ymin + (r->ymax - r->ymin)/2;
1146         return swf_ShapeAddGradientFillStyle(s, &m, &gradient->gradient, gradient->radial);
1147     }  else if (parseColor2(name, &color)) {
1148         return swf_ShapeAddSolidFillStyle(s, &color);
1149     } else {
1150         syntaxerror("not a color/fillstyle: %s", name);
1151         return 0;
1152     }
1153 }
1154
1155 RGBA black={r:0,g:0,b:0,a:0};
1156 void s_box(char*name, int width, int height, RGBA color, int linewidth, char*texture)
1157 {
1158     SRECT r,r2;
1159     SHAPE* s;
1160     int ls1=0,fs1=0;
1161     r2.xmin = 0;
1162     r2.ymin = 0;
1163     r2.xmax = width;
1164     r2.ymax = height;
1165     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
1166     swf_ShapeNew(&s);
1167     if(linewidth) {
1168         linewidth = linewidth>=20?linewidth-20:0;
1169         ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
1170     }
1171     if(texture)
1172         fs1 = addFillStyle(s, &r2, texture);
1173
1174     swf_SetU16(tag,id);
1175     r.xmin = r2.xmin-linewidth/2;
1176     r.ymin = r2.ymin-linewidth/2;
1177     r.xmax = r2.xmax+linewidth/2;
1178     r.ymax = r2.ymax+linewidth/2;
1179     swf_SetRect(tag,&r);
1180     swf_SetShapeHeader(tag,s);
1181     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
1182     swf_ShapeSetLine(tag,s,width,0);
1183     swf_ShapeSetLine(tag,s,0,height);
1184     swf_ShapeSetLine(tag,s,-width,0);
1185     swf_ShapeSetLine(tag,s,0,-height);
1186     swf_ShapeSetEnd(tag);
1187     swf_ShapeFree(s);
1188
1189     s_addcharacter(name, id, tag, r);
1190     incrementid();
1191 }
1192
1193 void s_filled(char*name, char*outlinename, RGBA color, int linewidth, char*texture)
1194 {
1195     SRECT rect,r2;
1196     SHAPE* s;
1197     int ls1,fs1=0;
1198     outline_t* outline;
1199     outline = dictionary_lookup(&outlines, outlinename);
1200     if(!outline) {
1201         syntaxerror("outline %s not defined", outlinename);
1202     }
1203     r2 = outline->bbox;
1204
1205     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
1206     swf_ShapeNew(&s);
1207     if(linewidth) {
1208         linewidth = linewidth>=20?linewidth-20:0;
1209         ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
1210     }
1211     if(texture)
1212         fs1 = addFillStyle(s, &r2, texture);
1213
1214     swf_SetU16(tag,id);
1215     rect.xmin = r2.xmin-linewidth/2;
1216     rect.ymin = r2.ymin-linewidth/2;
1217     rect.xmax = r2.xmax+linewidth/2;
1218     rect.ymax = r2.ymax+linewidth/2;
1219
1220     swf_SetRect(tag,&rect);
1221     swf_SetShapeStyles(tag, s);
1222     swf_ShapeCountBits(s,0,0);
1223     swf_RecodeShapeData(outline->shape->data, outline->shape->bitlen, outline->shape->bits.fill, outline->shape->bits.line,
1224                         &s->data,             &s->bitlen,             s->bits.fill,              s->bits.line);
1225     swf_SetShapeBits(tag, s);
1226     swf_SetBlock(tag, s->data, (s->bitlen+7)/8);
1227     swf_ShapeFree(s);
1228
1229     s_addcharacter(name, id, tag, rect);
1230     incrementid();
1231 }
1232
1233 void s_circle(char*name, int r, RGBA color, int linewidth, char*texture)
1234 {
1235     SRECT rect,r2;
1236     SHAPE* s;
1237     int ls1=0,fs1=0;
1238     r2.xmin = r2.ymin = 0;
1239     r2.xmax = 2*r;
1240     r2.ymax = 2*r;
1241
1242     tag = swf_InsertTag(tag, ST_DEFINESHAPE3);
1243     swf_ShapeNew(&s);
1244     if(linewidth) {
1245         linewidth = linewidth>=20?linewidth-20:0;
1246         ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
1247     }
1248     if(texture)
1249         fs1 = addFillStyle(s, &r2, texture);
1250     swf_SetU16(tag,id);
1251     rect.xmin = r2.xmin-linewidth/2;
1252     rect.ymin = r2.ymin-linewidth/2;
1253     rect.xmax = r2.xmax+linewidth/2;
1254     rect.ymax = r2.ymax+linewidth/2;
1255
1256     swf_SetRect(tag,&rect);
1257     swf_SetShapeHeader(tag,s);
1258     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
1259     swf_ShapeSetCircle(tag, s, r,r,r,r);
1260     swf_ShapeSetEnd(tag);
1261     swf_ShapeFree(s);
1262
1263     s_addcharacter(name, id, tag, rect);
1264     incrementid();
1265 }
1266
1267 void s_textshape(char*name, char*fontname, float size, char*_text)
1268 {
1269     int g;
1270     U8*text = (U8*)_text;
1271     outline_t* outline;
1272
1273     SWFFONT*font;
1274     font = dictionary_lookup(&fonts, fontname);
1275     if(!font)
1276         syntaxerror("font \"%s\" not known!", fontname);
1277
1278     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
1279         warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
1280         s_box(name, 0, 0, black, 20, 0);
1281         return;
1282     }
1283     g = font->ascii2glyph[text[0]];
1284
1285     outline = malloc(sizeof(outline_t));
1286     memset(outline, 0, sizeof(outline_t));
1287     outline->shape = font->glyph[g].shape;
1288     outline->bbox = font->layout->bounds[g];
1289
1290     {
1291         drawer_t draw;
1292         swf_Shape11DrawerInit(&draw, 0);
1293         swf_DrawText(&draw, font, (int)(size*100), _text);
1294         draw.finish(&draw);
1295         outline->shape = swf_ShapeDrawerToShape(&draw);
1296         outline->bbox = swf_ShapeDrawerGetBBox(&draw);
1297         draw.dealloc(&draw);
1298     }
1299
1300     if(dictionary_lookup(&outlines, name))
1301         syntaxerror("outline %s defined twice", name);
1302     dictionary_put2(&outlines, name, outline);
1303 }
1304
1305 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
1306 {
1307     SRECT r;
1308     MATRIX _m,*m=0;
1309     SWFFONT*font;
1310     font = dictionary_lookup(&fonts, fontname);
1311     if(!font)
1312         syntaxerror("font \"%s\" not known!", fontname);
1313
1314     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
1315     swf_SetU16(tag, id);
1316     if(!font->numchars) {
1317         s_box(name, 0, 0, black, 20, 0);
1318         return;
1319     }
1320     r = swf_SetDefineText(tag, font, &color, text, size);
1321
1322     if(stack[0].swf->fileVersion >= 8) {
1323         tag = swf_InsertTag(tag, ST_CSMTEXTSETTINGS);
1324         swf_SetU16(tag, id);
1325         swf_SetU8(tag, /*grid*/(1<<3)|/*flashtype*/0x40);
1326         swf_SetU32(tag, 0);//thickness
1327         swf_SetU32(tag, 0);//sharpness
1328         swf_SetU8(tag, 0);//reserved
1329     }
1330
1331     s_addcharacter(name, id, tag, r);
1332     incrementid();
1333 }
1334
1335 void s_quicktime(char*name, char*url)
1336 {
1337     SRECT r;
1338     MATRIX _m,*m=0;
1339
1340     memset(&r, 0, sizeof(r));
1341
1342     tag = swf_InsertTag(tag, ST_DEFINEMOVIE);
1343     swf_SetU16(tag, id);
1344     swf_SetString(tag, url);
1345
1346     s_addcharacter(name, id, tag, r);
1347     incrementid();
1348 }
1349
1350 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)
1351 {
1352     SWFFONT*font = 0;
1353     EditTextLayout layout;
1354     SRECT r;
1355
1356     if(fontname && *fontname) {
1357         flags |= ET_USEOUTLINES;
1358         font = dictionary_lookup(&fonts, fontname);
1359         if(!font)
1360             syntaxerror("font \"%s\" not known!", fontname);
1361     }
1362     tag = swf_InsertTag(tag, ST_DEFINEEDITTEXT);
1363     swf_SetU16(tag, id);
1364     layout.align = align;
1365     layout.leftmargin = 0;
1366     layout.rightmargin = 0;
1367     layout.indent = 0;
1368     layout.leading = 0;
1369     r.xmin = 0;
1370     r.ymin = 0;
1371     r.xmax = width;
1372     r.ymax = height;
1373
1374     swf_SetEditText(tag, flags, r, text, color, maxlength, font?font->id:0, size, &layout, variable);
1375
1376     s_addcharacter(name, id, tag, r);
1377     incrementid();
1378 }
1379
1380 /* type: either "jpeg" or "png"
1381  */
1382 void s_image(char*name, char*type, char*filename, int quality)
1383 {
1384     /* an image is actually two folded: 1st bitmap, 2nd character.
1385        Both of them can be used separately */
1386
1387     /* step 1: the bitmap */
1388     SRECT r;
1389     int imageID = id;
1390     int width, height;
1391     if(!strcmp(type,"jpeg")) {
1392 #ifndef HAVE_JPEGLIB
1393         warning("no jpeg support compiled in");
1394         s_box(name, 0, 0, black, 20, 0);
1395         return;
1396 #else
1397         tag = swf_InsertTag(tag, ST_DEFINEBITSJPEG2);
1398         swf_SetU16(tag, imageID);
1399
1400         if(swf_SetJPEGBits(tag, filename, quality) < 0) {
1401             syntaxerror("Image \"%s\" not found, or contains errors", filename);
1402         }
1403
1404         swf_GetJPEGSize(filename, &width, &height);
1405
1406         r.xmin = 0;
1407         r.ymin = 0;
1408         r.xmax = width*20;
1409         r.ymax = height*20;
1410
1411         s_addimage(name, id, tag, r);
1412         incrementid();
1413 #endif
1414     } else if(!strcmp(type,"png")) {
1415         RGBA*data = 0;
1416         swf_SetU16(tag, imageID);
1417
1418         getPNG(filename, &width, &height, (unsigned char**)&data);
1419
1420         if(!data) {
1421             syntaxerror("Image \"%s\" not found, or contains errors", filename);
1422         }
1423
1424         /*tag = swf_AddImage(tag, imageID, data, width, height, quality)*/
1425         tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
1426         swf_SetU16(tag, imageID);
1427         swf_SetLosslessImage(tag, data, width, height);
1428     free(data);
1429
1430         r.xmin = 0;
1431         r.ymin = 0;
1432         r.xmax = width*20;
1433         r.ymax = height*20;
1434         s_addimage(name, id, tag, r);
1435         incrementid();
1436     } else {
1437         warning("image type \"%s\" not supported yet!", type);
1438         s_box(name, 0, 0, black, 20, 0);
1439         return;
1440     }
1441
1442     /* step 2: the character */
1443     tag = swf_InsertTag(tag, ST_DEFINESHAPE); // todo: should be defineshape2/3 once images can be transparent.(?)
1444     swf_SetU16(tag, id);
1445     swf_ShapeSetBitmapRect(tag, imageID, width, height);
1446
1447     s_addcharacter(name, id, tag, r);
1448     incrementid();
1449 }
1450
1451 void s_getBitmapSize(char*name, int*width, int*height)
1452 {
1453     character_t* image = dictionary_lookup(&images, name);
1454     gradient_t* gradient = dictionary_lookup(&gradients,name);
1455     if(image) {
1456         *width = image->size.xmax;
1457         *height = image->size.ymax;
1458         return;
1459     }
1460     if(gradient) {
1461         /* internal SWF gradient size */
1462         if(gradient->radial) {
1463             *width = 16384;
1464             *height = 16384;
1465         } else {
1466             *width = 32768;
1467             *height = 32768;
1468         }
1469         return;
1470     }
1471     syntaxerror("No such bitmap/gradient: %s", name);
1472 }
1473
1474 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
1475 {
1476     if(dictionary_lookup(&textures, name))
1477         syntaxerror("texture %s defined twice", name);
1478     gradient_t* gradient = dictionary_lookup(&gradients, object);
1479     character_t* bitmap = dictionary_lookup(&images, object);
1480     texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
1481     parameters_t p;
1482     FILLSTYLE*fs = &texture->fs;
1483
1484     memset(&p, 0, sizeof(parameters_t));
1485
1486     if(bitmap) {
1487         fs->type = FILL_TILED;
1488         fs->id_bitmap = bitmap->id;
1489     } else if(gradient) {
1490         fs->type = gradient->radial?FILL_RADIAL:FILL_LINEAR;
1491         fs->gradient = gradient->gradient;
1492     }
1493     p.x = x;p.y = y;p.scalex = scalex;p.scaley = scaley;p.rotate=rotate;p.shear=shear;
1494     makeMatrix(&fs->m, &p);
1495     if(gradient && !gradient->radial) {
1496         MATRIX m = fs->m;
1497         SPOINT p1,p2;
1498         m.tx = 0;
1499         m.ty = 0;
1500         p1.x = 16384;
1501         p1.y = 16384;
1502         p2 = swf_TurnPoint(p1, &m);
1503         fs->m.tx += p2.x;
1504         fs->m.ty += p2.y;
1505     }
1506     if(bitmap) {
1507         fs->m.sx *= 20;
1508         fs->m.sy *= 20;
1509     }
1510
1511     dictionary_put2(&textures, name, texture);
1512 }
1513
1514 void s_font(char*name, char*filename)
1515 {
1516     SWFFONT* font;
1517     font = dictionary_lookup(&fonts, name);
1518     if(0)
1519     {
1520         /* fix the layout. Only needed for old fonts */
1521         int t;
1522         for(t=0;t<font->numchars;t++) {
1523             font->glyph[t].advance = 0;
1524         }
1525         font->layout = 0;
1526         swf_FontCreateLayout(font);
1527     }
1528     font->id = id;
1529         swf_FontReduce_swfc(font);
1530     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
1531     swf_FontSetDefine2(tag, font);
1532     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1533     swf_SetU16(tag, 1);
1534     swf_SetU16(tag, id);
1535     swf_SetString(tag, name);
1536
1537     incrementid();
1538 }
1539
1540
1541
1542 typedef struct _sound_t
1543 {
1544     U16 id;
1545     TAG*tag;
1546 } sound_t;
1547
1548 void s_sound(char*name, char*filename)
1549 {
1550     struct WAV wav, wav2;
1551     struct MP3 mp3;
1552     sound_t* sound;
1553     U16*samples = NULL;
1554     unsigned numsamples = 1;
1555     unsigned blocksize = 1152;
1556     int is_mp3 = 0;
1557
1558     if(dictionary_lookup(&sounds, name))
1559         syntaxerror("sound %s defined twice", name);
1560
1561     if(wav_read(&wav, filename))
1562     {
1563         int t;
1564         wav_convert2mono(&wav, &wav2, 44100);
1565         samples = (U16*)wav2.data;
1566         numsamples = wav2.size/2;
1567         free(wav.data);
1568 #ifdef WORDS_BIGENDIAN
1569         /* swap bytes */
1570         for(t=0;t<numsamples;t++)
1571             samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
1572 #endif
1573     }
1574     else
1575         if(mp3_read(&mp3, filename))
1576         {
1577             fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
1578             blocksize = 1;
1579             is_mp3 = 1;
1580         }
1581         else
1582         {
1583             warning("Couldn't read WAV/MP3 file \"%s\"", filename);
1584             samples = 0;
1585             numsamples = 0;
1586         }
1587
1588     if(numsamples%blocksize != 0)
1589     {
1590         // apply padding, so that block is a multiple of blocksize
1591         int numblocks = (numsamples+blocksize-1)/blocksize;
1592         int numsamples2;
1593         U16* samples2;
1594         numsamples2 = numblocks * blocksize;
1595         samples2 = malloc(sizeof(U16)*numsamples2);
1596         memcpy(samples2, samples, numsamples*sizeof(U16));
1597         memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
1598         numsamples = numsamples2;
1599         free(samples);
1600         samples = samples2;
1601     }
1602
1603     tag = swf_InsertTag(tag, ST_DEFINESOUND);
1604     swf_SetU16(tag, id); //id
1605     if(is_mp3)
1606     {
1607         swf_SetSoundDefineMP3(
1608                 tag, mp3.data, mp3.size,
1609                 mp3.SampRate,
1610                 mp3.Channels,
1611                 mp3.NumFrames);
1612         mp3_clear(&mp3);
1613     }
1614     else
1615         swf_SetSoundDefine(tag, samples, numsamples);
1616
1617     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
1618     swf_SetU16(tag, id);
1619     swf_SetString(tag, name);
1620     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
1621     swf_SetU16(tag, 1);
1622     swf_SetU16(tag, id);
1623     swf_SetString(tag, name);
1624
1625     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
1626     sound->tag = tag;
1627     sound->id = id;
1628
1629     dictionary_put2(&sounds, name, sound);
1630
1631     incrementid();
1632
1633     if (samples)
1634         free(samples);
1635 }
1636
1637 static char* gradient_getToken(const char**p)
1638 {
1639     const char*start;
1640     char*result;
1641     while(**p && strchr(" \t\n\r", **p)) {
1642         (*p)++;
1643     }
1644     start = *p;
1645     while(**p && !strchr(" \t\n\r", **p)) {
1646         (*p)++;
1647     }
1648     result = malloc((*p)-start+1);
1649     memcpy(result,start,(*p)-start+1);
1650     result[(*p)-start] = 0;
1651     return result;
1652 }
1653
1654 float parsePercent(char*str);
1655 RGBA parseColor(char*str);
1656
1657 GRADIENT parseGradient(const char*str)
1658 {
1659     GRADIENT gradient;
1660     int lastpos = -1;
1661     const char* p = str;
1662     memset(&gradient, 0, sizeof(GRADIENT));
1663     gradient.ratios = rfx_calloc(16*sizeof(U8));
1664     gradient.rgba = rfx_calloc(16*sizeof(RGBA));
1665
1666     while(*p)
1667     {
1668         char*posstr,*colorstr;
1669         int pos;
1670         RGBA color;
1671         posstr = gradient_getToken(&p);
1672         if(!*posstr)
1673         {
1674             free(posstr);
1675             break;
1676         }
1677         pos = (int)(parsePercent(posstr)*255.0);
1678         if(pos == lastpos)
1679             pos++;
1680         if(!*p)
1681         {
1682             rfx_free(gradient.ratios);
1683             rfx_free(gradient.rgba);
1684             free(posstr);
1685             syntaxerror("Error in shape data: Color expected after %s", posstr);
1686         }
1687         colorstr = gradient_getToken(&p);
1688         color = parseColor(colorstr);
1689         if(gradient.num == 16)
1690         {
1691             warning("gradient record too big- max size is 16, rest ignored");
1692             break;
1693         }
1694         gradient.ratios[gradient.num] = pos;
1695         gradient.rgba[gradient.num] = color;
1696         gradient.num++;
1697         free(posstr);
1698         free(colorstr);
1699         lastpos = pos;
1700         }
1701     return gradient;
1702 }
1703
1704 FILTERLIST* parseFilters(char* list)
1705 {
1706     if (!strcmp(list, "no_filters"))
1707         return 0;
1708     FILTER* f;
1709     FILTERLIST* f_list = (FILTERLIST*)malloc(sizeof(FILTERLIST));
1710     f_list->num = 0;
1711     char* f_start = list;
1712     char* f_end;
1713     while (f_start)
1714     {
1715         f_end = strchr(f_start, ',');
1716         if (f_end)
1717             *f_end = '\0';
1718         f = dictionary_lookup(&filters, f_start);
1719         if (!f)
1720         {
1721             free(f_list);
1722             syntaxerror("unknown filter %s", f_start);
1723         }
1724         if (f_list->num == 8)
1725         {
1726             warning("too many filters in filterlist, no more than 8 please, rest ignored");
1727             break;
1728         }
1729         f_list->filter[f_list->num] = f;
1730         f_list->num++;
1731         if (f_end)
1732         {
1733             *f_end = ',';
1734             f_start = f_end + 1;
1735         }
1736         else
1737             f_start = 0;
1738     }
1739     return f_list;
1740 }
1741
1742 void s_gradient(char*name, const char*text, int radial, int rotate)
1743 {
1744     gradient_t* gradient;
1745     gradient = malloc(sizeof(gradient_t));
1746     memset(gradient, 0, sizeof(gradient_t));
1747     gradient->gradient = parseGradient(text);
1748     gradient->radial = radial;
1749     gradient->rotate = rotate;
1750
1751     dictionary_put2(&gradients, name, gradient);
1752 }
1753
1754 void s_gradientglow(char*name, char*gradient, float blurx, float blury,
1755                     float angle, float distance, float strength, char innershadow,
1756                     char knockout, char composite, char ontop, int passes)
1757 {
1758     if(dictionary_lookup(&filters, name))
1759         syntaxerror("filter %s defined twice", name);
1760
1761     gradient_t* g = dictionary_lookup(&gradients, gradient);
1762     if(!g)
1763         syntaxerror("unknown gradient %s", gradient);
1764
1765     composite = 1;
1766
1767     FILTER_GRADIENTGLOW* filter = rfx_calloc(sizeof(FILTER_GRADIENTGLOW));
1768     filter->type = FILTERTYPE_GRADIENTGLOW;
1769     filter->gradient = &g->gradient;
1770     filter->blurx = blurx;
1771     filter->blury = blury;
1772     filter->strength = strength;
1773     filter->angle = angle;
1774     filter->distance = distance;
1775     filter->innershadow = innershadow;
1776     filter->knockout = knockout;
1777     filter->composite = composite;
1778     filter->ontop = ontop;
1779     filter->passes = passes;
1780
1781     dictionary_put2(&filters, name, filter);
1782 }
1783
1784 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)
1785 {
1786     if(dictionary_lookup(&filters, name))
1787         syntaxerror("filter %s defined twice", name);
1788
1789     composite = 1;
1790     FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
1791     filter->type = FILTERTYPE_DROPSHADOW;
1792     filter->color= color;
1793     filter->blurx = blurx;
1794     filter->blury = blury;
1795     filter->strength = strength;
1796     filter->angle = angle;
1797     filter->distance = distance;
1798     filter->innershadow = innershadow;
1799     filter->knockout = knockout;
1800     filter->composite = composite;
1801     filter->passes = passes;
1802
1803     dictionary_put2(&filters, name, filter);
1804 }
1805
1806 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)
1807 {
1808     if(dictionary_lookup(&filters, name))
1809         syntaxerror("filter %s defined twice", name);
1810
1811     composite = 1;
1812     FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
1813     filter->type = FILTERTYPE_BEVEL;
1814     filter->shadow = shadow;
1815     filter->highlight = highlight;
1816     filter->blurx = blurx;
1817     filter->blury = blury;
1818     filter->strength = strength;
1819     filter->angle = angle;
1820     filter->distance = distance;
1821     filter->innershadow = innershadow;
1822     filter->knockout = knockout;
1823     filter->composite = composite;
1824     filter->ontop = ontop;
1825     filter->passes = passes;
1826
1827     dictionary_put2(&filters, name, filter);
1828 }
1829
1830 void s_blur(char*name, double blurx, double blury, int passes)
1831 {
1832     if(dictionary_lookup(&filters, name))
1833         syntaxerror("filter %s defined twice", name);
1834
1835     FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
1836     filter->type = FILTERTYPE_BLUR;
1837     filter->blurx = blurx;
1838     filter->blury = blury;
1839     filter->passes = passes;
1840
1841     dictionary_put2(&filters, name, filter);
1842 }
1843
1844 void s_action(const char*text)
1845 {
1846     ActionTAG* a = 0;
1847     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1848     if(!a)
1849     {
1850         swf_ActionFree(a);
1851         syntaxerror("Couldn't compile ActionScript");
1852     }
1853
1854     tag = swf_InsertTag(tag, ST_DOACTION);
1855
1856     swf_ActionSet(tag, a);
1857
1858     swf_ActionFree(a);
1859 }
1860
1861 void s_initaction(const char*character, const char*text)
1862 {
1863     ActionTAG* a = 0;
1864     character_t*c = 0;
1865     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
1866     if(!a)
1867     {
1868         swf_ActionFree(a);
1869         syntaxerror("Couldn't compile ActionScript");
1870     }
1871
1872     c = (character_t*)dictionary_lookup(&characters, character);
1873
1874     tag = swf_InsertTag(tag, ST_DOINITACTION);
1875     swf_SetU16(tag, c->id);
1876     swf_ActionSet(tag, a);
1877
1878     swf_ActionFree(a);
1879 }
1880
1881 int s_swf3action(char*name, char*action)
1882 {
1883     ActionTAG* a = 0;
1884     instance_t* object = 0;
1885     if(name)
1886         object = (instance_t*)dictionary_lookup(&instances, name);
1887     if(!object && name && *name) {
1888         /* we have a name, but couldn't find it. Abort. */
1889         return 0;
1890     }
1891     a = action_SetTarget(0, name);
1892     if(!strcmp(action, "nextframe")) a = action_NextFrame(a);
1893     else if(!strcmp(action, "previousframe")) a = action_PreviousFrame(a);
1894     else if(!strcmp(action, "stop")) a = action_Stop(a);
1895     else if(!strcmp(action, "play")) a = action_Play(a);
1896     a = action_SetTarget(a, "");
1897     a = action_End(a);
1898
1899     tag = swf_InsertTag(tag, ST_DOACTION);
1900     swf_ActionSet(tag, a);
1901     swf_ActionFree(a);
1902     return 1;
1903 }
1904
1905 void s_outline(char*name, char*format, char*source)
1906 {
1907     if(dictionary_lookup(&outlines, name))
1908         syntaxerror("outline %s defined twice", name);
1909
1910     outline_t* outline;
1911
1912     drawer_t draw;
1913     SHAPE* shape;
1914     SHAPE2* shape2;
1915     SRECT bounds;
1916
1917     //swf_Shape10DrawerInit(&draw, 0);
1918     swf_Shape11DrawerInit(&draw, 0);
1919
1920     draw_string(&draw, source);
1921     draw.finish(&draw);
1922     shape = swf_ShapeDrawerToShape(&draw);
1923     bounds = swf_ShapeDrawerGetBBox(&draw);
1924     draw.dealloc(&draw);
1925
1926     outline = (outline_t*)rfx_calloc(sizeof(outline_t));
1927     outline->shape = shape;
1928     outline->bbox = bounds;
1929
1930     dictionary_put2(&outlines, name, outline);
1931 }
1932
1933 int s_playsound(char*name, int loops, int nomultiple, int stop)
1934 {
1935     sound_t* sound;
1936     SOUNDINFO info;
1937     if(!name)
1938         return 0;
1939     sound = dictionary_lookup(&sounds, name);
1940     if(!sound)
1941         return 0;
1942
1943     tag = swf_InsertTag(tag, ST_STARTSOUND);
1944     swf_SetU16(tag, sound->id); //id
1945     memset(&info, 0, sizeof(info));
1946     info.stop = stop;
1947     info.loops = loops;
1948     info.nomultiple = nomultiple;
1949     swf_SetSoundInfo(tag, &info);
1950     return 1;
1951 }
1952
1953 void s_includeswf(char*name, char*filename)
1954 {
1955     int f;
1956     SWF swf;
1957     TAG* ftag;
1958     SRECT r;
1959     TAG* s;
1960     int level = 0;
1961     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
1962     f = open(filename,O_RDONLY|O_BINARY);
1963     if (f<0) {
1964         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
1965         s_box(name, 0, 0, black, 20, 0);
1966         return;
1967     }
1968     if (swf_ReadSWF(f,&swf)<0) {
1969         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
1970         s_box(name, 0, 0, black, 20, 0);
1971         return;
1972     }
1973     close(f);
1974
1975     /* FIXME: The following sets the bounding Box for the character.
1976               It is wrong for two reasons:
1977               a) It may be too small (in case objects in the movie clip at the borders)
1978               b) it may be too big (because the poor movie never got autocropped)
1979     */
1980     r = swf.movieSize;
1981
1982     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
1983     swf_SetU16(tag, id);
1984     swf_SetU16(tag, swf.frameCount);
1985
1986     swf_Relocate(&swf, idmap);
1987
1988     ftag = swf.firstTag;
1989     level = 1;
1990     while(ftag) {
1991         int t;
1992         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
1993             if(cutout[t] == ftag->id) {
1994                 ftag = ftag->next;
1995                 continue;
1996             }
1997         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
1998             level++;
1999         if(ftag->id == ST_END)
2000             level--;
2001         if(!level)
2002             break;
2003
2004         if(ftag->id != ST_SETBACKGROUNDCOLOR) {
2005             /* We simply dump all tags right after the sprite
2006                header, relying on the fact that swf_OptimizeTagOrder() will
2007                sort things out for us later.
2008                We also rely on the fact that the imported SWF is well-formed.
2009              */
2010             tag = swf_InsertTag(tag, ftag->id);
2011             swf_SetBlock(tag, ftag->data, ftag->len);
2012         }
2013
2014         ftag = ftag->next;
2015     }
2016     if(!ftag)
2017         syntaxerror("Included file %s contains errors", filename);
2018     tag = swf_InsertTag(tag, ST_END);
2019
2020     swf_FreeTags(&swf);
2021
2022     s_addcharacter(name, id, tag, r);
2023     incrementid();
2024 }
2025 SRECT s_getCharBBox(char*name)
2026 {
2027     character_t* c = dictionary_lookup(&characters, name);
2028     if(!c) syntaxerror("character '%s' unknown(2)", name);
2029     return c->size;
2030 }
2031 SRECT s_getInstanceBBox(char*name)
2032 {
2033     instance_t * i = dictionary_lookup(&instances, name);
2034     character_t * c;
2035     if(!i) syntaxerror("instance '%s' unknown(4)", name);
2036     c = i->character;
2037     if(!c) syntaxerror("internal error(5)");
2038     return c->size;
2039 }
2040 void s_getParameters(char*name, parameters_t* p)
2041 {
2042     instance_t * i = dictionary_lookup(&instances, name);
2043     if(!i)
2044         syntaxerror("instance '%s' unknown(10)", name);
2045     if (change_sets_all)
2046         readParameters(i->history, p, currentframe);
2047     else
2048         *p = i->parameters;
2049 }
2050 void s_startclip(char*instance, char*character, parameters_t p)
2051 {
2052     character_t* c = dictionary_lookup(&characters, character);
2053     instance_t* i;
2054     MATRIX m;
2055     if(!c) {
2056         syntaxerror("character %s not known", character);
2057     }
2058     i = s_addinstance(instance, c, currentdepth);
2059     i->parameters = p;
2060     m = s_instancepos(i->character->size, &p);
2061
2062     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2063     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
2064     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
2065
2066     stack[stackpos].tag = tag;
2067     stack[stackpos].type = 2;
2068     stackpos++;
2069
2070     currentdepth++;
2071 }
2072 void s_endClip()
2073 {
2074     SWFPLACEOBJECT p;
2075     stackpos--;
2076     swf_SetTagPos(stack[stackpos].tag, 0);
2077     swf_GetPlaceObject(stack[stackpos].tag, &p);
2078     p.clipdepth = currentdepth;
2079     p.name = 0;
2080     swf_ClearTag(stack[stackpos].tag);
2081     swf_SetPlaceObject(stack[stackpos].tag, &p);
2082     currentdepth++;
2083 }
2084
2085 void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
2086 {
2087     history_begin(i->history, "x", currentframe, tag, p->x);
2088     history_begin(i->history, "y", currentframe, tag, p->y);
2089     history_begin(i->history, "scalex", currentframe, tag, p->scalex);
2090     history_begin(i->history, "scaley", currentframe, tag, p->scaley);
2091     history_begin(i->history, "cxform.r0", currentframe, tag, p->cxform.r0);
2092     history_begin(i->history, "cxform.g0", currentframe, tag, p->cxform.g0);
2093     history_begin(i->history, "cxform.b0", currentframe, tag, p->cxform.b0);
2094     history_begin(i->history, "cxform.a0", currentframe, tag, p->cxform.a0);
2095     history_begin(i->history, "cxform.r1", currentframe, tag, p->cxform.r1);
2096     history_begin(i->history, "cxform.g1", currentframe, tag, p->cxform.g1);
2097     history_begin(i->history, "cxform.b1", currentframe, tag, p->cxform.b1);
2098     history_begin(i->history, "cxform.a1", currentframe, tag, p->cxform.a1);
2099     history_begin(i->history, "rotate", currentframe, tag, p->rotate);
2100     history_begin(i->history, "shear", currentframe, tag, p->shear);
2101     history_begin(i->history, "pivot.x", currentframe, tag, p->pivot.x);
2102     history_begin(i->history, "pivot.y", currentframe, tag, p->pivot.y);
2103     history_begin(i->history, "pin.x", currentframe, tag, p->pin.x);
2104     history_begin(i->history, "pin.y", currentframe, tag, p->pin.y);
2105     history_begin(i->history, "blendmode", currentframe, tag, p->blendmode);
2106     history_beginFilter(i->history, currentframe, tag, p->filters);
2107     history_begin(i->history, "flags", currentframe, tag, 0);
2108 }
2109
2110 void s_put(char*instance, char*character, parameters_t p)
2111 {
2112     character_t* c = dictionary_lookup(&characters, character);
2113     instance_t* i;
2114     MATRIX m;
2115     if(!c)
2116         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
2117
2118     i = s_addinstance(instance, c, currentdepth);
2119     i->parameters = p;
2120     m = s_instancepos(i->character->size, &p);
2121
2122     if(p.blendmode || p.filters)
2123     {
2124         if(stack[0].swf->fileVersion < 8)
2125         {
2126             if(p.blendmode)
2127                 warning("blendmodes only supported for flash version>=8");
2128             else
2129                 warning("filters only supported for flash version>=8");
2130         }
2131         tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
2132         }
2133     else
2134         tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
2135     setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
2136     setStartparameters(i, &p, tag);
2137     currentdepth++;
2138 }
2139
2140 void recordChanges(history_t* history, parameters_t p, int changeFunction, interpolation_t* inter)
2141 {
2142     if (p.set & SF_X)
2143         history_remember(history, "x", currentframe, changeFunction, p.x, inter);
2144     if (p.set & SF_Y)
2145         history_remember(history, "y", currentframe, changeFunction, p.y, inter);
2146     if (p.set & SF_SCALEX)
2147         history_remember(history, "scalex", currentframe, changeFunction, p.scalex, inter);
2148     if (p.set & SF_SCALEY)
2149         history_remember(history, "scaley", currentframe, changeFunction, p.scaley, inter);
2150     if (p.set & SF_CX_R)
2151     {
2152         history_remember(history, "cxform.r0", currentframe, changeFunction, p.cxform.r0, inter);
2153         history_remember(history, "cxform.r1", currentframe, changeFunction, p.cxform.r1, inter);
2154     }
2155     if (p.set & SF_CX_G)
2156     {
2157         history_remember(history, "cxform.g0", currentframe, changeFunction, p.cxform.g0, inter);
2158         history_remember(history, "cxform.g1", currentframe, changeFunction, p.cxform.g1, inter);
2159     }
2160     if (p.set & SF_CX_B)
2161     {
2162         history_remember(history, "cxform.b0", currentframe, changeFunction, p.cxform.b0, inter);
2163         history_remember(history, "cxform.b1", currentframe, changeFunction, p.cxform.b1, inter);
2164     }
2165     if (p.set & SF_CX_A)
2166     {
2167         history_remember(history, "cxform.a0", currentframe, changeFunction, p.cxform.a0, inter);
2168         history_remember(history, "cxform.a1", currentframe, changeFunction, p.cxform.a1, inter);
2169     }
2170     if (p.set & SF_ROTATE)
2171         history_remember(history, "rotate", currentframe, changeFunction, p.rotate, inter);
2172     if (p.set & SF_SHEAR)
2173         history_remember(history, "shear", currentframe, changeFunction, p.shear, inter);
2174     if (p.set & SF_PIVOT)
2175     {
2176         history_remember(history, "pivot.x", currentframe, changeFunction, p.pivot.x, inter);
2177         history_remember(history, "pivot.y", currentframe, changeFunction, p.pivot.y, inter);
2178     }
2179     if (p.set & SF_PIN)
2180     {
2181         history_remember(history, "pin.x", currentframe, changeFunction, p.pin.x, inter);
2182         history_remember(history, "pin.y", currentframe, changeFunction, p.pin.y, inter);
2183     }
2184     if (p.set & SF_BLEND)
2185         history_remember(history, "blendmode", currentframe, changeFunction, p.blendmode, inter);
2186     if (p.set & SF_FILTER)
2187         history_rememberFilter(history, currentframe, changeFunction, p.filters, inter);
2188 }
2189
2190 void s_jump(char* instance, parameters_t p)
2191 {
2192     instance_t* i = dictionary_lookup(&instances, instance);
2193     if(!i)
2194         syntaxerror("instance %s not known", instance);
2195     recordChanges(i->history, p, CF_JUMP, 0);
2196 }
2197
2198 void s_change(char*instance, parameters_t p, interpolation_t* inter)
2199 {
2200     instance_t* i = dictionary_lookup(&instances, instance);
2201     if(!i)
2202         syntaxerror("instance %s not known", instance);
2203     recordChanges(i->history, p, CF_CHANGE, inter);
2204 }
2205
2206 void s_sweep(char* instance, parameters_t p, float radius, int clockwise, int short_arc, interpolation_t* inter)
2207 {
2208     instance_t* i = dictionary_lookup(&instances, instance);
2209     if(!i)
2210         syntaxerror("instance %s not known", instance);
2211     history_rememberSweep(i->history, currentframe, p.x, p.y, radius, clockwise, short_arc, inter);
2212 }
2213
2214 void s_toggle(char* instance, U16 flagsOn, U16 flagsOff)
2215 {
2216     instance_t* i = dictionary_lookup(&instances, instance);
2217     if (!i)
2218         syntaxerror("instance %s not known", instance);
2219     U16 flags = (U16)history_value(i->history, currentframe, "flags");
2220     flags |= flagsOn;
2221     flags &= flagsOff;
2222     history_remember(i->history, "flags", currentframe, CF_JUMP, flags, 0);
2223 }
2224
2225 void s_delinstance(char*instance)
2226 {
2227     instance_t* i = dictionary_lookup(&instances, instance);
2228     if(!i)
2229         syntaxerror("instance %s not known", instance);
2230     writeInstance(i);
2231     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
2232     swf_SetU16(tag, i->depth);
2233     dictionary_del(&instances, instance);
2234 }
2235
2236 void s_schange(char*instance, parameters_t p, interpolation_t* inter)
2237 {
2238     instance_t* i = dictionary_lookup(&instances, instance);
2239     if(!i)
2240         syntaxerror("instance %s not known", instance);
2241     recordChanges(i->history, p, CF_SCHANGE, inter);
2242 }
2243
2244 void s_end()
2245 {
2246     if(!stackpos)
2247         syntaxerror(".end unexpected");
2248     switch (stack[stackpos-1].type)
2249     {
2250         case 0:
2251             s_endSWF();
2252             break;
2253         case 1:
2254             s_endSprite();
2255             break;
2256         case 2:
2257             s_endClip();
2258             break;
2259         case 3:
2260             s_endButton();
2261             break;
2262         default:
2263             syntaxerror("internal error 1");
2264     }
2265 }
2266
2267 // ------------------------------------------------------------------------
2268
2269 typedef int command_func_t(map_t*args);
2270
2271 SRECT parseBox(char*str)
2272 {
2273     SRECT r = {0,0,0,0};
2274     float xmin, xmax, ymin, ymax;
2275     char*x = strchr(str, 'x');
2276     char*d1=0,*d2=0;
2277     if(!strcmp(str, "autocrop")) {
2278         r.xmin = r.ymin = r.xmax = r.ymax = 0;
2279         return r;
2280     }
2281     if(!x) goto error;
2282     d1 = strchr(x+1, ':');
2283     if(d1)
2284         d2 = strchr(d1+1, ':');
2285     if(!d1) {
2286         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
2287             goto error;
2288         xmin = ymin = 0;
2289     }
2290     else if(d1 && !d2) {
2291         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
2292             goto error;
2293         xmax += xmin;
2294         ymin = 0;
2295     }
2296     else {
2297         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
2298             goto error;
2299         xmax += xmin;
2300         ymax += ymin;
2301     }
2302     r.xmin = (SCOORD)(xmin*20);
2303     r.ymin = (SCOORD)(ymin*20);
2304     r.xmax = (SCOORD)(xmax*20);
2305     r.ymax = (SCOORD)(ymax*20);
2306     return r;
2307 error:
2308     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
2309     return r;
2310 }
2311 float parseFloat(char*str)
2312 {
2313     return atof(str);
2314 }
2315 int parseInt(char*str)
2316 {
2317     int t;
2318     int l=strlen(str);
2319     int s=0;
2320     if(str[0]=='+' || str[0]=='-')
2321         s++;
2322
2323     for(t=s;t<l;t++)
2324         if(str[t]<'0' || str[t]>'9')
2325             syntaxerror("Not an Integer: \"%s\"", str);
2326     return atoi(str);
2327 }
2328 int parseTwip(char*str)
2329 {
2330     char*dot;
2331     int sign=1;
2332     if(str[0]=='+' || str[0]=='-') {
2333         if(str[0]=='-')
2334             sign = -1;
2335         str++;
2336     }
2337     dot = strchr(str, '.');
2338     if(!dot) {
2339         int l=strlen(str);
2340         int t;
2341         return sign*parseInt(str)*20;
2342     } else {
2343         char* old = strdup(str);
2344         int l=strlen(dot+1);
2345         char*s;
2346         *dot++ = 0;
2347         for(s=str;s<dot-1;s++)
2348         if(*s<'0' || *s>'9')
2349         {
2350             free(old);
2351             syntaxerror("Not a coordinate: \"%s\"", str);
2352         }
2353     for(s=dot;*s;s++)
2354         if(*s<'0' || *s>'9')
2355         {
2356             free(old);
2357             syntaxerror("Not a coordinate: \"%s\"", str);
2358         }
2359         if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
2360             dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
2361             dot[2] = 0;
2362             l=2;
2363             warning("precision loss: %s converted to twip: %s.%s", old, str, dot);
2364         }
2365         free(old);
2366         if(l==0)
2367             return sign*(atoi(str)*20);
2368         if(l==1)
2369             return sign*(atoi(str)*20+atoi(dot)*2);
2370         if(l==2)
2371             return sign*(atoi(str)*20+atoi(dot)/5);
2372     }
2373     return 0;
2374 }
2375
2376 int parseArc(char* str)
2377 {
2378     if (!strcmp(str, "short"))
2379         return 1;
2380     if (!strcmp(str, "long"))
2381         return 0;
2382     syntaxerror("invalid value for the arc parameter: %s", str);
2383     return 1;
2384 }
2385
2386 int parseDir(char* str)
2387 {
2388     if (!strcmp(str, "clockwise"))
2389         return 1;
2390     if (!strcmp(str, "counterclockwise"))
2391         return 0;
2392     syntaxerror("invalid value for the dir parameter: %s", str);
2393     return 1;
2394 }
2395
2396 int isPoint(char*str)
2397 {
2398     if(strchr(str, '('))
2399         return 1;
2400     else
2401         return 0;
2402 }
2403
2404 SPOINT parsePoint(char*str)
2405 {
2406     SPOINT p;
2407     char tmp[80];
2408     int l = strlen(str);
2409     char*comma = strchr(str, ',');
2410     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
2411         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
2412     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
2413     p.x = parseTwip(tmp);
2414     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
2415     p.y = parseTwip(tmp);
2416     return p;
2417 }
2418
2419 int parseColor2(char*str, RGBA*color)
2420 {
2421     int l = strlen(str);
2422     int r,g,b,a;
2423     int t;
2424
2425     struct {unsigned char r,g,b;char*name;} colors[] =
2426     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
2427     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
2428     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
2429     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
2430     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
2431     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
2432     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
2433     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
2434     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
2435     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
2436     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
2437     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
2438
2439     a=255;r=g=b=0;
2440
2441     if(str[0]=='#' && (l==7 || l==9)) {
2442         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
2443             return 0;
2444         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
2445             return 0;
2446         color->r = r; color->g = g; color->b = b; color->a = a;
2447         return 1;
2448     }
2449     int len=strlen(str);
2450     U8 alpha = 255;
2451     if(strchr(str, '/')) {
2452         len = strchr(str, '/')-str;
2453         sscanf(str+len+1,"%02x", &alpha);
2454     }
2455     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
2456         if(!strncmp(str, colors[t].name, len)) {
2457             r = colors[t].r;
2458             g = colors[t].g;
2459             b = colors[t].b;
2460             a = alpha;
2461             color->r = r; color->g = g; color->b = b; color->a = a;
2462             return 1;
2463         }
2464     return 0;
2465
2466 }
2467 RGBA parseColor(char*str)
2468 {
2469     RGBA c;
2470     if(!parseColor2(str, &c))
2471         syntaxerror("Expression '%s' is not a color", str);
2472     return c;
2473 }
2474
2475 typedef struct _muladd {
2476     S16 mul;
2477     S16 add;
2478 } MULADD;
2479
2480 MULADD parseMulAdd(char*str)
2481 {
2482     float add, mul;
2483     char* str2 = (char*)malloc(strlen(str)+5);
2484     int i;
2485     MULADD m;
2486     strcpy(str2, str);
2487     strcat(str2, " 0");
2488     add = 0;
2489     mul = 1.0;
2490     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
2491     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
2492     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
2493     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
2494     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
2495     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
2496     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
2497     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
2498     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
2499     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
2500     else {
2501         syntaxerror("'%s' is not a valid color transform expression", str);
2502     }
2503     m.add = (int)(add*256);
2504     m.mul = (int)(mul*256);
2505     free(str2);
2506     return m;
2507 }
2508
2509 MULADD mergeMulAdd(MULADD m1, MULADD m2)
2510 {
2511     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
2512     double m = ((double)m1.mul*(double)m2.mul)/256.0;
2513     MULADD r;
2514     if(a<-32768) a=-32768;
2515     if(a>32767) a=32767;
2516     if(m<-32768) m=-32768;
2517     if(m>32767) m=32767;
2518     r.add = a;
2519     r.mul = (int)m;
2520     return r;
2521 }
2522
2523 float parsePxOrPercent(char*fontname, char*str)
2524 {
2525     int l = strlen(str);
2526     if(strchr(str, '%'))
2527         return parsePercent(str);
2528     if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
2529         float p = atof(str);
2530         return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
2531     }
2532     syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
2533     return 0;
2534 }
2535
2536 float parsePercent(char*str)
2537 {
2538     int l = strlen(str);
2539     if(!l)
2540         return 1.0;
2541     if(str[l-1]=='%') {
2542         return atoi(str)/100.0;
2543     }
2544     syntaxerror("Expression '%s' is not a percentage", str);
2545     return 0;
2546 }
2547 int isPercent(char*str)
2548 {
2549     return str[strlen(str)-1]=='%';
2550 }
2551 int parseNewSize(char*str, int size)
2552 {
2553     if(isPercent(str))
2554         return parsePercent(str)*size;
2555     else
2556         return (int)(atof(str)*20);
2557 }
2558
2559 int isColor(char*str)
2560 {
2561     RGBA c;
2562     return parseColor2(str, &c);
2563 }
2564
2565 static char* lu(map_t* args, char*name)
2566 {
2567     char* value = map_lookup(args, name);
2568     if(!value) {
2569         map_dump(args, stdout, "");
2570         syntaxerror("internal error 2: value %s should be set", name);
2571     }
2572     return value;
2573 }
2574
2575 static int c_flash(map_t*args)
2576 {
2577     char* filename = map_lookup(args, "filename");
2578     char* compressstr = lu(args, "compress");
2579     char* change_modestr = lu(args, "change-sets-all");
2580     SRECT bbox = parseBox(lu(args, "bbox"));
2581     int version = parseInt(lu(args, "version"));
2582     int fps = (int)(parseFloat(lu(args, "fps"))*256);
2583     RGBA color = parseColor(lu(args, "background"));
2584     int compress = 0;
2585
2586     if(!filename || !*filename) {
2587         /* for compatibility */
2588         filename = map_lookup(args, "name");
2589         if(!filename || !*filename) {
2590             filename = 0;
2591         } else {
2592             //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2593             msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2594         }
2595     }
2596
2597     if(!filename || override_outputname)
2598         filename = outputname;
2599
2600     if(!strcmp(compressstr, "default"))
2601         compress = version>=6;
2602     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2603         compress = 1;
2604     else if(!strcmp(compressstr, "no"))
2605         compress = 0;
2606     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2607
2608     if(!strcmp(change_modestr, "yes"))
2609         change_sets_all = 1;
2610     else
2611         if(strcmp(change_modestr, "no"))
2612             syntaxerror("value \"%s\" not supported for the change-sets-all argument", change_modestr);
2613
2614     s_swf(filename, bbox, version, fps, compress, color);
2615     return 0;
2616 }
2617 int isRelative(char*str)
2618 {
2619     return !strncmp(str, "<plus>", 6) ||
2620            !strncmp(str, "<minus>", 7);
2621 }
2622 char* getOffset(char*str)
2623 {
2624     if(!strncmp(str, "<plus>", 6))
2625         return str+6;
2626     if(!strncmp(str, "<minus>", 7))
2627         return str+7;
2628     syntaxerror("internal error (347)");
2629     return 0;
2630 }
2631 int getSign(char*str)
2632 {
2633     if(!strncmp(str, "<plus>", 6))
2634         return 1;
2635     if(!strncmp(str, "<minus>", 7))
2636         return -1;
2637     syntaxerror("internal error (348)");
2638     return 0;
2639 }
2640 static dictionary_t points;
2641 static mem_t mpoints;
2642 int points_initialized = 0;
2643
2644 static int c_interpolation(map_t *args)
2645 {
2646     int i;
2647     char* name = lu(args, "name");
2648     if (dictionary_lookup(&interpolations, name))
2649         syntaxerror("interpolation %s defined twice", name);
2650
2651     interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
2652     char* functionstr = lu(args, "function");
2653     inter->function = 0;
2654     for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
2655         if (!strcmp(functionstr,interpolationFunctions[i]))
2656         {
2657             inter->function = i + 1;
2658             break;
2659         }
2660     if (!inter->function)
2661         syntaxerror("unkown interpolation function %s", functionstr);
2662     inter->speed = parseFloat(lu(args, "speed"));
2663     inter->amplitude = parseTwip(lu(args, "amplitude"));
2664     inter->growth = parseFloat(lu(args, "growth"));
2665     inter->bounces = parseInt(lu(args, "bounces"));
2666     inter->damping = parseFloat(lu(args, "damping"));
2667     inter->slope = parseFloat(lu(args, "slope"));
2668
2669     dictionary_put2(&interpolations, name, inter);
2670     return 0;
2671 }
2672
2673 SPOINT getPoint(SRECT r, char*name)
2674 {
2675     int l=0;
2676     if(!strcmp(name, "center")) {
2677         SPOINT p;
2678         p.x = (r.xmin + r.xmax)/2;
2679         p.y = (r.ymin + r.ymax)/2;
2680         return p;
2681     }
2682     if (!strcmp(name, "bottom-center")) {
2683         SPOINT p;
2684         p.x = (r.xmin + r.xmax)/2;
2685         p.y = r.ymax;
2686         return p;
2687     }
2688     if (!strcmp(name, "top-center")) {
2689         SPOINT p;
2690         p.x = (r.xmin + r.xmax)/2;
2691         p.y = r.ymin;
2692         return p;
2693     }
2694     if (!strcmp(name, "top-left")) {
2695         SPOINT p;
2696         p.x = r.xmin;
2697         p.y = r.ymin;
2698         return p;
2699     }
2700     if (!strcmp(name, "top-right")) {
2701         SPOINT p;
2702         p.x = r.xmax;
2703         p.y = r.ymin;
2704         return p;
2705     }
2706     if (!strcmp(name, "bottom-right")) {
2707         SPOINT p;
2708         p.x = r.xmax;
2709         p.y = r.ymax;
2710         return p;
2711     }
2712     if (!strcmp(name, "bottom-left")) {
2713         SPOINT p;
2714         p.x = r.xmin;
2715         p.y = r.ymax;
2716         return p;
2717     }
2718     if (!strcmp(name, "left-center")) {
2719         SPOINT p;
2720         p.x = r.xmin;
2721         p.y = (r.ymin + r.ymax)/2;
2722         return p;
2723     }
2724     if (!strcmp(name, "right-center")) {
2725         SPOINT p;
2726         p.x = r.xmax;
2727         p.y = (r.ymin + r.ymax)/2;
2728         return p;
2729     }
2730
2731
2732     if(points_initialized)
2733         l = (int)dictionary_lookup(&points, name);
2734     if(l==0) {
2735         syntaxerror("Invalid point: \"%s\".", name);
2736     }
2737     l--;
2738     return *(SPOINT*)&mpoints.buffer[l];
2739 }
2740
2741
2742 static int texture2(char*name, char*object, map_t*args, int errors)
2743 {
2744     SPOINT pos,size;
2745     char*xstr = map_lookup(args, "x");
2746     char*ystr = map_lookup(args, "y");
2747     char*widthstr = map_lookup(args, "width");
2748     char*heightstr = map_lookup(args, "height");
2749     char*scalestr = map_lookup(args, "scale");
2750     char*scalexstr = map_lookup(args, "scalex");
2751     char*scaleystr = map_lookup(args, "scaley");
2752     char*rotatestr = map_lookup(args, "rotate");
2753     char* shearstr = map_lookup(args, "shear");
2754     char* radiusstr = map_lookup(args, "r");
2755     float x=0,y=0;
2756     float scalex = 1.0, scaley = 1.0;
2757     float rotate=0, shear=0;
2758     float r = 0;
2759     if(!*xstr && !*ystr) {
2760         if(errors)
2761             syntaxerror("x and y must be set");
2762         return 0;
2763     }
2764     if(*scalestr && (*scalexstr || *scaleystr)) {
2765         syntaxerror("scale and scalex/scaley can't both be set");
2766         return 0;
2767     }
2768     if((*widthstr || *heightstr) && *radiusstr) {
2769         syntaxerror("width/height and radius can't both be set");
2770     }
2771     if(*radiusstr) {
2772         widthstr = radiusstr;
2773         heightstr = radiusstr;
2774     }
2775     if(!*xstr) xstr="0";
2776     if(!*ystr) ystr="0";
2777     if(!*rotatestr) rotatestr="0";
2778     if(!*shearstr) shearstr="0";
2779
2780     if(*scalestr) {
2781         scalex = scaley = parsePercent(scalestr);
2782     } else if(*scalexstr || *scaleystr) {
2783         if(scalexstr) scalex = parsePercent(scalexstr);
2784         if(scaleystr) scaley = parsePercent(scaleystr);
2785     } else if(*widthstr || *heightstr) {
2786         int width=0;
2787         int height=0;
2788         s_getBitmapSize(object, &width, &height);
2789         if(*widthstr)
2790             scalex = (float)parseTwip(widthstr)/(float)width;
2791         if(*heightstr)
2792             scaley = (float)parseTwip(heightstr)/(float)height;
2793     }
2794     x = parseTwip(xstr);
2795     y = parseTwip(ystr);
2796     rotate = parseFloat(rotatestr);
2797     shear = parseFloat(shearstr);
2798
2799     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2800
2801     return 0;
2802 }
2803
2804 static int c_texture(map_t*args)
2805 {
2806     char*name = lu(args, "instance");
2807     char*object = lu(args, "character");
2808     return texture2(name, object, args, 1);
2809 }
2810
2811 static int c_gradient(map_t*args)
2812 {
2813     char*name = lu(args, "name");
2814     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2815     int rotate = parseInt(lu(args, "rotate"));
2816
2817     readToken();
2818     if(type != RAWDATA)
2819         syntaxerror("colon (:) expected");
2820
2821     if(dictionary_lookup(&gradients, name))
2822         syntaxerror("gradient %s defined twice", name);
2823
2824     s_gradient(name, text, radial, rotate);
2825
2826     /* check whether we also have placement information,
2827        which would make this a positioned gradient.
2828        If there is placement information, texture2() will
2829        add a texture, which has priority over the gradient.
2830      */
2831     texture2(name, name, args, 0);
2832     return 0;
2833 }
2834
2835 static char* checkFiltername(map_t* args)
2836 {
2837     char* name = lu(args, "name");
2838     if (strchr(name, ','))
2839         syntaxerror("the comma (,) is used to separate filters in filterlists. Please do not use in filternames.");
2840     return name;
2841 }
2842
2843 static int c_blur(map_t*args)
2844 {
2845     char*name = checkFiltername(args);
2846     char*blurstr = lu(args, "blur");
2847     char*blurxstr = lu(args, "blurx");
2848     char*blurystr = lu(args, "blury");
2849     float blurx=1.0, blury=1.0;
2850     if(blurstr[0]) {
2851         blurx = parseFloat(blurstr);
2852         blury = parseFloat(blurstr);
2853     }
2854     if(blurxstr[0])
2855         blurx = parseFloat(blurxstr);
2856     if(blurystr[0])
2857         blury = parseFloat(blurystr);
2858     int passes = parseInt(lu(args, "passes"));
2859     s_blur(name, blurx, blury, passes);
2860     return 0;
2861 }
2862
2863 static int c_gradientglow(map_t*args)
2864 {
2865     char*name = checkFiltername(args);
2866     char*gradient = lu(args, "gradient");
2867     char*blurstr = lu(args, "blur");
2868     char*blurxstr = lu(args, "blurx");
2869     char*blurystr = lu(args, "blury");
2870     float blurx=1.0, blury=1.0;
2871     if(blurstr[0]) {
2872         blurx = parseFloat(blurstr);
2873         blury = parseFloat(blurstr);
2874     }
2875     if(blurxstr[0])
2876         blurx = parseFloat(blurxstr);
2877     if(blurystr[0])
2878         blury = parseFloat(blurystr);
2879
2880     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2881     float distance = parseFloat(lu(args, "distance"));
2882     float strength = parseFloat(lu(args, "strength"));
2883     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2884     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2885     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2886     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2887     int passes = parseInt(lu(args, "passes"));
2888
2889     s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2890     return 0;
2891 }
2892
2893 static int c_dropshadow(map_t*args)
2894 {
2895     char*name = checkFiltername(args);
2896     RGBA color = parseColor(lu(args, "color"));
2897     char*blurstr = lu(args, "blur");
2898     char*blurxstr = lu(args, "blurx");
2899     char*blurystr = lu(args, "blury");
2900     float blurx=1.0, blury=1.0;
2901     if(blurstr[0]) {
2902         blurx = parseFloat(blurstr);
2903         blury = parseFloat(blurstr);
2904     }
2905     if(blurxstr[0])
2906         blurx = parseFloat(blurxstr);
2907     if(blurystr[0])
2908         blury = parseFloat(blurystr);
2909
2910     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2911     float distance = parseFloat(lu(args, "distance"));
2912     float strength = parseFloat(lu(args, "strength"));
2913     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2914     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2915     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2916     int passes = parseInt(lu(args, "passes"));
2917
2918     s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
2919     return 0;
2920 }
2921
2922 static int c_bevel(map_t*args)
2923 {
2924     char*name = checkFiltername(args);
2925     RGBA shadow = parseColor(lu(args, "shadow"));
2926     RGBA highlight = parseColor(lu(args, "highlight"));
2927     char*blurstr = lu(args, "blur");
2928     char*blurxstr = lu(args, "blurx");
2929     char*blurystr = lu(args, "blury");
2930     float blurx=1.0, blury=1.0;
2931     if(blurstr[0]) {
2932         blurx = parseFloat(blurstr);
2933         blury = parseFloat(blurstr);
2934     }
2935     if(blurxstr[0])
2936         blurx = parseFloat(blurxstr);
2937     if(blurystr[0])
2938         blury = parseFloat(blurystr);
2939
2940     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2941     float distance = parseFloat(lu(args, "distance"));
2942     float strength = parseFloat(lu(args, "strength"));
2943     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2944     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2945     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2946     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2947     int passes = parseInt(lu(args, "passes"));
2948
2949     s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2950     return 0;
2951 }
2952
2953 static int c_point(map_t*args)
2954 {
2955     char*name = lu(args, "name");
2956     int pos;
2957     string_t s1;
2958     SPOINT p;
2959     if(!points_initialized) {
2960         dictionary_init(&points);
2961         mem_init(&mpoints);
2962         points_initialized = 1;
2963     }
2964     p.x = parseTwip(lu(args, "x"));
2965     p.y = parseTwip(lu(args, "y"));
2966     pos = mem_put(&mpoints, &p, sizeof(p));
2967     string_set(&s1, name);
2968     pos++;
2969     dictionary_put(&points, s1, (void*)pos);
2970     return 0;
2971 }
2972 static int c_play(map_t*args)
2973 {
2974     char*name = lu(args, "name");
2975     char*loop = lu(args, "loop");
2976     char*nomultiple = lu(args, "nomultiple");
2977     int nm = 0;
2978     if(!strcmp(nomultiple, "nomultiple"))
2979         nm = 1;
2980     else
2981         nm = parseInt(nomultiple);
2982
2983     if(s_playsound(name, parseInt(loop), nm, 0)) {
2984         return 0;
2985     } else if(s_swf3action(name, "play")) {
2986         return 0;
2987     }
2988     return 0;
2989 }
2990
2991 static int c_stop(map_t*args)
2992 {
2993     char*name = map_lookup(args, "name");
2994
2995     if(s_playsound(name, 0,0,1))
2996         return 0;
2997     else if(s_swf3action(name, "stop"))
2998         return 0;
2999     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
3000         return 0;
3001 }
3002
3003 static int c_nextframe(map_t*args)
3004 {
3005     char*name = lu(args, "name");
3006
3007     if(s_swf3action(name, "nextframe")) {
3008         return 0;
3009     }
3010     syntaxerror("I don't know anything about movie \"%s\"", name);
3011     return 0;
3012 }
3013
3014 static int c_previousframe(map_t*args)
3015 {
3016     char*name = lu(args, "name");
3017
3018     if(s_swf3action(name, "previousframe")) {
3019         return 0;
3020     }
3021     syntaxerror("I don't know anything about movie \"%s\"", name);
3022     return 0;
3023 }
3024
3025 static int c_movement(map_t*args, int type)
3026 {
3027     char*instance = lu(args, "name");
3028
3029     char* xstr="";
3030     char* ystr="";
3031     SRECT oldbbox;
3032     parameters_t p;
3033     U16 set = 0x0000;
3034
3035     xstr = lu(args, "x");
3036     ystr = lu(args, "y");
3037
3038     s_getParameters(instance, &p);
3039
3040     /* x,y position */
3041     if(xstr[0])
3042     {
3043         if(isRelative(xstr))
3044         {
3045             if(type == PT_PUT || type == PT_STARTCLIP)
3046                 syntaxerror("relative x values not allowed for initial put or startclip");
3047             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3048         }
3049         else
3050         {
3051             p.x = parseTwip(xstr);
3052         }
3053         set = set | SF_X;
3054      }
3055     if(ystr[0])
3056     {
3057         if(isRelative(ystr))
3058         {
3059             if(type == PT_PUT || type == PT_STARTCLIP)
3060                 syntaxerror("relative y values not allowed for initial put or startclip");
3061             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3062         }
3063         else
3064         {
3065             p.y = parseTwip(ystr);
3066         }
3067         set = set | SF_Y;
3068     }
3069
3070     if (change_sets_all)
3071         set = SF_ALL;
3072     p.set = set;
3073
3074     switch (type)
3075     {
3076         case PT_MOVE:
3077             {
3078                 char* interstr = lu(args, "interpolation");
3079                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3080                 if (!inter)
3081                     syntaxerror("unkown interpolation %s", interstr);
3082                 s_change(instance, p, inter);
3083             }
3084             break;
3085         case PT_SMOVE:
3086             {
3087                 char* interstr = lu(args, "interpolation");
3088                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3089                 if (!inter)
3090                     syntaxerror("unkown interpolation %s", interstr);
3091                 s_schange(instance, p, inter);
3092             }
3093             break;
3094         case PT_SWEEP:
3095             {
3096                 char* rstr = lu(args, "r");
3097                 int radius = parseTwip(rstr);
3098                 if (radius <= 0)
3099                         syntaxerror("sweep not possible: radius must be greater than 0.");
3100                 char* dirstr = lu(args, "dir");
3101                 int clockwise = parseDir(dirstr);
3102                 char* arcstr = lu(args, "arc");
3103                 int short_arc = parseArc(arcstr);
3104                 char* interstr = lu(args, "interpolation");
3105                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3106                 if (!inter)
3107                     syntaxerror("unkown interpolation %s", interstr);
3108                 s_sweep(instance, p, radius, clockwise, short_arc, inter);
3109             }
3110             break;
3111         }
3112     return 0;
3113 }
3114
3115 static int c_placement(map_t*args, int type)
3116 {
3117     char*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
3118     char*character = 0;
3119
3120     char* luminancestr = lu(args, "luminance");
3121     char* scalestr = lu(args, "scale");
3122     char* scalexstr = lu(args, "scalex");
3123     char* scaleystr = lu(args, "scaley");
3124     char* rotatestr = lu(args, "rotate");
3125     char* shearstr = lu(args, "shear");
3126     char* xstr="", *pivotstr="";
3127     char* ystr="", *anglestr="";
3128     char*above = lu(args, "above"); /*FIXME*/
3129     char*below = lu(args, "below");
3130     char* rstr = lu(args, "red");
3131     char* gstr = lu(args, "green");
3132     char* bstr = lu(args, "blue");
3133     char* astr = lu(args, "alpha");
3134     char* pinstr = lu(args, "pin");
3135     char* as = map_lookup(args, "as");
3136     char* blendmode = lu(args, "blend");
3137     char* filterstr = lu(args, "filter");
3138     U8 blend;
3139     MULADD r,g,b,a;
3140     float oldwidth;
3141     float oldheight;
3142     SRECT oldbbox;
3143     MULADD luminance;
3144     parameters_t p;
3145     U16 set = 0x0000;
3146
3147     if(type==9)
3148     { // (?) .rotate  or .arcchange
3149         pivotstr = lu(args, "pivot");
3150         anglestr = lu(args, "angle");
3151     }
3152     else
3153     {
3154         xstr = lu(args, "x");
3155         ystr = lu(args, "y");
3156     }
3157
3158     if(luminancestr[0])
3159         luminance = parseMulAdd(luminancestr);
3160     else
3161     {
3162         luminance.add = 0;
3163         luminance.mul = 256;
3164     }
3165
3166     if(scalestr[0])
3167     {
3168         if(scalexstr[0]||scaleystr[0])
3169             syntaxerror("scalex/scaley and scale cannot both be set");
3170         scalexstr = scaleystr = scalestr;
3171     }
3172
3173     if(type == PT_PUT || type == PT_STARTCLIP)  {
3174         // put or startclip
3175         character = lu(args, "character");
3176         parameters_clear(&p);
3177     } else if (type == PT_BUTTON) {
3178         character = lu(args, "name");
3179         parameters_clear(&p);
3180         // button's show
3181     } else {
3182         s_getParameters(instance, &p);
3183     }
3184
3185     /* x,y position */
3186     if(xstr[0])
3187     {
3188         if(isRelative(xstr))
3189         {
3190             if(type == PT_PUT || type == PT_STARTCLIP)
3191                 syntaxerror("relative x values not allowed for initial put or startclip");
3192             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3193         }
3194         else
3195         {
3196             p.x = parseTwip(xstr);
3197         }
3198         set = set | SF_X;
3199      }
3200     if(ystr[0])
3201     {
3202         if(isRelative(ystr))
3203         {
3204             if(type == PT_PUT || type == PT_STARTCLIP)
3205                 syntaxerror("relative y values not allowed for initial put or startclip");
3206             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3207         }
3208         else
3209         {
3210             p.y = parseTwip(ystr);
3211         }
3212         set = set | SF_Y;
3213         }
3214
3215     /* scale, scalex, scaley */
3216     if(character)
3217         oldbbox = s_getCharBBox(character);
3218     else
3219         oldbbox = s_getInstanceBBox(instance);
3220     oldwidth = oldbbox.xmax - oldbbox.xmin;
3221     oldheight = oldbbox.ymax - oldbbox.ymin;
3222     if(scalexstr[0])
3223     {
3224         if(oldwidth==0)
3225             p.scalex = 1.0;
3226         else
3227             if(scalexstr[0])
3228                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
3229         set = set | SF_SCALEX;
3230     }
3231    if(scaleystr[0])
3232    {
3233         if(oldheight==0)
3234             p.scaley = 1.0;
3235         else
3236             if(scaleystr[0])
3237                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
3238         set = set | SF_SCALEY;
3239    }
3240
3241     /* rotation */
3242     if(rotatestr[0])
3243     {
3244         if(isRelative(rotatestr))
3245             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
3246         else
3247             p.rotate = parseFloat(rotatestr);
3248         set = set | SF_ROTATE;
3249         }
3250
3251     /* shearing */
3252     if(shearstr[0])
3253     {
3254         if(isRelative(shearstr))
3255             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
3256         else
3257             p.shear = parseFloat(shearstr);
3258         set = set | SF_SHEAR;
3259     }
3260
3261     if(pivotstr[0])
3262     {
3263         if(isPoint(pivotstr))
3264             p.pivot = parsePoint(pivotstr);
3265         else
3266             p.pivot = getPoint(oldbbox, pivotstr);
3267         set = set | SF_PIVOT;
3268     }
3269
3270     if(pinstr[0])
3271     {
3272         if(isPoint(pinstr))
3273             p.pin = parsePoint(pinstr);
3274         else
3275             p.pin = getPoint(oldbbox, pinstr);
3276         set = set | SF_PIN;
3277     }
3278
3279     /* color transform */
3280
3281     if(rstr[0] || luminancestr[0])
3282     {
3283         MULADD r;
3284         if(rstr[0])
3285             r = parseMulAdd(rstr);
3286         else
3287         {
3288             r.add = p.cxform.r0;
3289             r.mul = p.cxform.r1;
3290         }
3291         r = mergeMulAdd(r, luminance);
3292         p.cxform.r0 = r.mul;
3293         p.cxform.r1 = r.add;
3294         set = set | SF_CX_R;
3295         }
3296     if(gstr[0] || luminancestr[0])
3297     {
3298         MULADD g;
3299         if(gstr[0])
3300             g = parseMulAdd(gstr);
3301         else
3302         {
3303             g.add = p.cxform.g0;
3304             g.mul = p.cxform.g1;
3305         }
3306         g = mergeMulAdd(g, luminance);
3307         p.cxform.g0 = g.mul;
3308         p.cxform.g1 = g.add;
3309         set = set | SF_CX_G;
3310     }
3311     if(bstr[0] || luminancestr[0])
3312     {
3313         MULADD b;
3314         if(bstr[0])
3315             b = parseMulAdd(bstr);
3316         else
3317         {
3318             b.add = p.cxform.b0;
3319             b.mul = p.cxform.b1;
3320         }
3321         b = mergeMulAdd(b, luminance);
3322         p.cxform.b0 = b.mul;
3323         p.cxform.b1 = b.add;
3324         set = set | SF_CX_B;
3325     }
3326     if(astr[0])
3327     {
3328         MULADD a = parseMulAdd(astr);
3329         p.cxform.a0 = a.mul;
3330         p.cxform.a1 = a.add;
3331         set = set | SF_CX_A;
3332     }
3333
3334     if(blendmode[0])
3335     {
3336         int t;
3337         blend = 255;
3338         for(t = 0; blendModeNames[t]; t++)
3339         {
3340             if(!strcmp(blendModeNames[t], blendmode))
3341             {
3342                 blend = t;
3343                 break;
3344             }
3345         }
3346         if(blend == 255)
3347         {
3348             syntaxerror("unknown blend mode: '%s'", blendmode);
3349         }
3350         p.blendmode = blend;
3351         set = set | SF_BLEND;
3352         }
3353
3354     if(filterstr[0])
3355     {
3356         p.filters = parseFilters(filterstr);
3357         set = set | SF_FILTER;
3358     }
3359
3360     if (type == PT_CHANGE && set & (SF_X | SF_Y))
3361         warning("As of version 0.8.2 using the .change command to modify an \
3362 object's position on the stage is considered deprecated. Future \
3363 versions may consider x and y parameters for the .change command \
3364 to be illegal; please use the .move command.");
3365
3366     if (change_sets_all)
3367         set = SF_ALL;
3368     p.set = set;
3369
3370     switch (type)
3371     {
3372         case PT_PUT:
3373             s_put(instance, character, p);
3374             break;
3375         case PT_CHANGE:
3376             {
3377                 char* interstr = lu(args, "interpolation");
3378                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3379                 if (!inter)
3380                     syntaxerror("unkown interpolation %s", interstr);
3381                 s_change(instance, p, inter);
3382             }
3383             break;
3384         case PT_SCHANGE:
3385             {
3386                 char* interstr = lu(args, "interpolation");
3387                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3388                 if (!inter)
3389                     syntaxerror("unkown interpolation %s", interstr);
3390                 s_schange(instance, p, inter);
3391             }
3392             break;
3393         case PT_JUMP:
3394             s_jump(instance, p);
3395             break;
3396         case PT_STARTCLIP:
3397             s_startclip(instance, character, p);
3398             break;
3399         case PT_BUTTON:
3400             if(as && as[0])
3401                 s_buttonput(character, as, p);
3402             else
3403                 s_buttonput(character, "shape", p);
3404             break;
3405 //        default:
3406         }
3407     return 0;
3408 }
3409 static int c_put(map_t*args)
3410 {
3411     c_placement(args, PT_PUT);
3412     return 0;
3413 }
3414 static int c_change(map_t*args)
3415 {
3416     if (currentframe == 0)
3417         warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
3418     c_placement(args, PT_CHANGE);
3419     return 0;
3420 }
3421 static int c_schange(map_t*args)
3422 {
3423     c_placement(args, PT_SCHANGE);
3424     return 0;
3425 }
3426 static int c_move(map_t* args)
3427 {
3428     c_movement(args, PT_MOVE);
3429     return 0;
3430 }
3431 static int c_smove(map_t* args)
3432 {
3433     c_movement(args, PT_SMOVE);
3434     return 0;
3435 }
3436 static int c_sweep(map_t* args)
3437 {
3438     c_movement(args, PT_SWEEP);
3439     return 0;
3440 }
3441 static int c_arcchange(map_t*args)
3442 {
3443     c_placement(args, 0);
3444     return 0;
3445 }
3446 static int c_jump(map_t*args)
3447 {
3448     c_placement(args, PT_JUMP);
3449     return 0;
3450 }
3451 static int c_startclip(map_t*args)
3452 {
3453     c_placement(args, PT_STARTCLIP);
3454     return 0;
3455 }
3456 static int c_show(map_t*args)
3457 {
3458     c_placement(args, PT_BUTTON);
3459     return 0;
3460 }
3461 static int c_toggle(map_t* args)
3462 {
3463     char*instance = lu(args, "name");
3464     U16 flagsOn = 0x0000, flagsOff = 0xffff;
3465     char* alignstr = lu(args, "fixed_alignment");
3466     if (!strcmp(alignstr, "on"))
3467         flagsOn += IF_FIXED_ALIGNMENT;
3468     else
3469         if (!strcmp(alignstr, "off"))
3470             flagsOff -= IF_FIXED_ALIGNMENT;
3471         else
3472             syntaxerror("values for toggle must be \"on\" or \"off\". %s is not legal.", alignstr);
3473     s_toggle(instance, flagsOn, flagsOff);
3474     return 0;
3475 }
3476 static int c_del(map_t*args)
3477 {
3478     char*instance = lu(args, "name");
3479     s_delinstance(instance);
3480     return 0;
3481 }
3482 static int c_end(map_t*args)
3483 {
3484     s_end();
3485     return 0;
3486 }
3487 static int c_sprite(map_t*args)
3488 {
3489     char* name = lu(args, "name");
3490     s_sprite(name);
3491     return 0;
3492 }
3493 static int c_frame(map_t*args)
3494 {
3495     char*framestr = lu(args, "n");
3496     char*cutstr = lu(args, "cut");
3497
3498     char*name = lu(args, "name");
3499     char*anchor = lu(args, "anchor");
3500     char buf[40];
3501
3502     if(!strcmp(anchor, "anchor") && !*name)
3503         name = framestr;
3504
3505     int frame;
3506     int cut = 0;
3507     if(strcmp(cutstr, "no"))
3508         cut = 1;
3509     if(isRelative(framestr)) {
3510         frame = s_getframe();
3511         if(getSign(framestr)<0)
3512             syntaxerror("relative frame expressions must be positive");
3513         frame += parseInt(getOffset(framestr));
3514     }
3515     else {
3516         frame = parseInt(framestr);
3517         if(s_getframe() >= frame
3518                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
3519             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
3520     }
3521     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
3522     return 0;
3523 }
3524 static int c_primitive(map_t*args)
3525 {
3526     char*name = lu(args, "name");
3527     char*command = lu(args, "commandname");
3528     int width=0, height=0, r=0;
3529     int linewidth = parseTwip(lu(args, "line"));
3530     char*colorstr = lu(args, "color");
3531     RGBA color = parseColor(colorstr);
3532     char*fillstr = lu(args, "fill");
3533     int dofill = 1;
3534     int type=0;
3535     char* font;
3536     char* text;
3537     char* outline=0;
3538     RGBA fill;
3539     if(!strcmp(command, "circle"))
3540         type = 1;
3541     else if(!strcmp(command, "filled"))
3542         type = 2;
3543
3544     if(type==0) {
3545         width = parseTwip(lu(args, "width"));
3546         height = parseTwip(lu(args, "height"));
3547     } else if (type==1) {
3548         r = parseTwip(lu(args, "r"));
3549     } else if (type==2) {
3550         outline = lu(args, "outline");
3551     }
3552
3553     if(!strcmp(fillstr, "fill"))
3554         fillstr = colorstr;
3555     if(!strcmp(fillstr, "none"))
3556         fillstr = 0;
3557     if(width<0 || height<0 || linewidth<0 || r<0)
3558         syntaxerror("values width, height, line, r must be positive");
3559
3560     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
3561     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
3562     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
3563     return 0;
3564 }
3565
3566 static int c_textshape(map_t*args)
3567 {
3568     char*name = lu(args, "name");
3569     char*text = lu(args, "text");
3570     char*font = lu(args, "font");
3571     float size = parsePxOrPercent(font, lu(args, "size"));
3572
3573     s_textshape(name, font, size, text);
3574     return 0;
3575 }
3576
3577 static int c_swf(map_t*args)
3578 {
3579     char*name = lu(args, "name");
3580     char*filename = lu(args, "filename");
3581     char*command = lu(args, "commandname");
3582     if(!strcmp(command, "shape"))
3583         warning("Please use .swf instead of .shape");
3584     s_includeswf(name, filename);
3585     return 0;
3586 }
3587
3588 static int c_font(map_t*args)
3589 {
3590     char*name = lu(args, "name");
3591     char*filename = lu(args, "filename");
3592     s_font(name, filename);
3593     return 0;
3594 }
3595
3596 static int c_sound(map_t*args)
3597 {
3598     char*name = lu(args, "name");
3599     char*filename = lu(args, "filename");
3600     s_sound(name, filename);
3601     return 0;
3602 }
3603
3604 static int c_text(map_t*args)
3605 {
3606     char*name = lu(args, "name");
3607     char*text = lu(args, "text");
3608     char*font = lu(args, "font");
3609     float size = parsePxOrPercent(font, lu(args, "size"));
3610     RGBA color = parseColor(lu(args, "color"));
3611     s_text(name, font, text, (int)(size*100), color);
3612     return 0;
3613 }
3614
3615 static int c_soundtrack(map_t*args)
3616 {
3617     return 0;
3618 }
3619
3620 static int c_quicktime(map_t*args)
3621 {
3622     char*name = lu(args, "name");
3623     char*url = lu(args, "url");
3624     s_quicktime(name, url);
3625     return 0;
3626 }
3627
3628 static int c_image(map_t*args)
3629 {
3630     char*command = lu(args, "commandname");
3631     char*name = lu(args, "name");
3632     char*filename = lu(args, "filename");
3633     if(!strcmp(command,"jpeg")) {
3634         int quality = (int)(parsePercent(lu(args, "quality"))*100);
3635         s_image(name, "jpeg", filename, quality);
3636     } else {
3637         s_image(name, "png", filename, 0);
3638     }
3639     return 0;
3640 }
3641
3642 static int c_outline(map_t*args)
3643 {
3644     char*name = lu(args, "name");
3645     char*format = lu(args, "format");
3646
3647     readToken();
3648     if(type != RAWDATA)
3649         syntaxerror("colon (:) expected");
3650
3651     s_outline(name, format, text);
3652     return 0;
3653 }
3654
3655 int fakechar(map_t*args)
3656 {
3657     char*name = lu(args, "name");
3658     s_box(name, 0, 0, black, 20, 0);
3659     return 0;
3660 }
3661
3662 static int c_egon(map_t*args) {return fakechar(args);}
3663 static int c_button(map_t*args) {
3664     char*name = lu(args, "name");
3665     s_button(name);
3666     return 0;
3667 }
3668 static int current_button_flags = 0;
3669 static int c_on_press(map_t*args)
3670 {
3671     char*position = lu(args, "position");
3672     char*action = "";
3673     if(!strcmp(position, "inside")) {
3674         current_button_flags |= BC_OVERUP_OVERDOWN;
3675     } else if(!strcmp(position, "outside")) {
3676         //current_button_flags |= BC_IDLE_OUTDOWN;
3677         syntaxerror("IDLE_OVERDOWN not supported by SWF");
3678     } else if(!strcmp(position, "anywhere")) {
3679         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
3680     }
3681     readToken();
3682     if(type == RAWDATA) {
3683         action = text;
3684         s_buttonaction(current_button_flags, action);
3685         current_button_flags = 0;
3686     }
3687     else
3688         pushBack();
3689     return 0;
3690 }
3691 static int c_on_release(map_t*args)
3692 {
3693     char*position = lu(args, "position");
3694     char*action = "";
3695     if(!strcmp(position, "inside")) {
3696         current_button_flags |= BC_OVERDOWN_OVERUP;
3697     } else if(!strcmp(position, "outside")) {
3698         current_button_flags |= BC_OUTDOWN_IDLE;
3699     } else if(!strcmp(position, "anywhere")) {
3700         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
3701     }
3702     readToken();
3703     if(type == RAWDATA) {
3704         action = text;
3705         s_buttonaction(current_button_flags, action);
3706         current_button_flags = 0;
3707     }
3708     else
3709         pushBack();
3710     return 0;
3711 }
3712 static int c_on_move_in(map_t*args)
3713 {
3714     char*position = lu(args, "state");
3715     char*action = "";
3716     if(!strcmp(position, "pressed")) {
3717         current_button_flags |= BC_OUTDOWN_OVERDOWN;
3718     } else if(!strcmp(position, "not_pressed")) {
3719         current_button_flags |= BC_IDLE_OVERUP;
3720     } else if(!strcmp(position, "any")) {
3721         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
3722     }
3723     readToken();
3724     if(type == RAWDATA) {
3725         action = text;
3726         s_buttonaction(current_button_flags, action);
3727         current_button_flags = 0;
3728     }
3729     else
3730         pushBack();
3731     return 0;
3732 }
3733 static int c_on_move_out(map_t*args)
3734 {
3735     char*position = lu(args, "state");
3736     char*action = "";
3737     if(!strcmp(position, "pressed")) {
3738         current_button_flags |= BC_OVERDOWN_OUTDOWN;
3739     } else if(!strcmp(position, "not_pressed")) {
3740         current_button_flags |= BC_OVERUP_IDLE;
3741     } else if(!strcmp(position, "any")) {
3742         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
3743     }
3744     readToken();
3745     if(type == RAWDATA) {
3746         action = text;
3747         s_buttonaction(current_button_flags, action);
3748         current_button_flags = 0;
3749     }
3750     else
3751         pushBack();
3752     return 0;
3753 }
3754 static int c_on_key(map_t*args)
3755 {
3756     char*key = lu(args, "key");
3757     char*action = "";
3758     if(strlen(key)==1) {
3759         /* ascii */
3760         if(key[0]>=32) {
3761             current_button_flags |= 0x4000 + (key[0]*0x200);
3762         } else {
3763             syntaxerror("invalid character: %c"+key[0]);
3764             return 1;
3765         }
3766     } else {
3767         /* TODO:
3768            <ctrl-x> = 0x200*(x-'a')
3769            esc = = 0x3600
3770            space = = 0x4000;
3771         */
3772         syntaxerror("invalid key: %s",key);
3773     }
3774     readToken();
3775     if(type == RAWDATA) {
3776         action = text;
3777         s_buttonaction(current_button_flags, action);
3778         current_button_flags = 0;
3779     }
3780     else
3781         pushBack();
3782     return 0;
3783 }
3784
3785 static int c_edittext(map_t*args)
3786 {
3787  //"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"},
3788     char*name = lu(args, "name");
3789     char*font = lu(args, "font");
3790     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
3791     int width = parseTwip(lu(args, "width"));
3792     int height = parseTwip(lu(args, "height"));
3793     char*text  = lu(args, "text");
3794     RGBA color = parseColor(lu(args, "color"));
3795     int maxlength = parseInt(lu(args, "maxlength"));
3796     char*variable = lu(args, "variable");
3797     char*passwordstr = lu(args, "password");
3798     char*wordwrapstr = lu(args, "wordwrap");
3799     char*multilinestr = lu(args, "multiline");
3800     char*htmlstr = lu(args, "html");
3801     char*noselectstr = lu(args, "noselect");
3802     char*readonlystr = lu(args, "readonly");
3803     char*borderstr = lu(args, "border");
3804     char*autosizestr = lu(args, "autosize");
3805     char*alignstr = lu(args, "align");
3806     int align = -1;
3807
3808     int flags = 0;
3809     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
3810     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
3811     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
3812     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
3813     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
3814     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
3815     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
3816     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
3817     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
3818     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
3819     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
3820     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
3821     else syntaxerror("Unknown alignment: %s", alignstr);
3822
3823     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
3824     return 0;
3825 }
3826
3827 static int c_morphshape(map_t*args) {return fakechar(args);}
3828 static int c_movie(map_t*args) {return fakechar(args);}
3829
3830 static char* readfile(const char*filename)
3831 {
3832     FILE*fi = fopen(filename, "rb");
3833     int l;
3834     char*text;
3835     if(!fi)
3836         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
3837     fseek(fi, 0, SEEK_END);
3838     l = ftell(fi);
3839     fseek(fi, 0, SEEK_SET);
3840     text = rfx_alloc(l+1);
3841     fread(text, l, 1, fi);
3842     text[l]=0;
3843     fclose(fi);
3844     return text;
3845 }
3846
3847 static int c_action(map_t*args)
3848 {
3849     char* filename  = map_lookup(args, "filename");
3850     if(!filename ||!*filename) {
3851         readToken();
3852         if(type != RAWDATA) {
3853             syntaxerror("colon (:) expected");
3854         }
3855         s_action(text);
3856     } else {
3857         s_action(readfile(filename));
3858     }
3859
3860     return 0;
3861 }
3862
3863 static int c_initaction(map_t*args)
3864 {
3865     char* character = lu(args, "name");
3866     char* filename  = map_lookup(args, "filename");
3867     if(!filename ||!*filename) {
3868         readToken();
3869         if(type != RAWDATA) {
3870             syntaxerror("colon (:) expected");
3871         }
3872         s_initaction(character, text);
3873     } else {
3874         s_initaction(character, readfile(filename));
3875     }
3876
3877     return 0;
3878 }
3879
3880 static struct {
3881     char*command;
3882     command_func_t* func;
3883     char*arguments;
3884 } arguments[] =
3885 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no"},
3886  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
3887  // "import" type stuff
3888  {"swf", c_swf, "name filename"},
3889  {"shape", c_swf, "name filename"},
3890  {"jpeg", c_image, "name filename quality=80%"},
3891  {"png", c_image, "name filename"},
3892  {"movie", c_movie, "name filename"},
3893  {"sound", c_sound, "name filename"},
3894  {"font", c_font, "name filename glyphs="},
3895  {"soundtrack", c_soundtrack, "filename"},
3896  {"quicktime", c_quicktime, "url"},
3897
3898     // generators of primitives
3899
3900  {"point", c_point, "name x=0 y=0"},
3901  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
3902  {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5 damping=2 slope=0"},
3903  {"outline", c_outline, "name format=simple"},
3904  {"textshape", c_textshape, "name font size=100% text"},
3905
3906     // filters
3907  {"blur", c_blur, "name blur= blurx= blury= passes=1"},
3908  {"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"},
3909  {"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"},
3910  {"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"},
3911
3912     // character generators
3913  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
3914  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
3915  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
3916
3917  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
3918  {"text", c_text, "name text font size=100% color=white"},
3919  {"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="},
3920  {"morphshape", c_morphshape, "name start end"},
3921  {"button", c_button, "name"},
3922     {"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="},
3923     {"on_press", c_on_press, "position=inside"},
3924     {"on_release", c_on_release, "position=anywhere"},
3925     {"on_move_in", c_on_move_in, "state=not_pressed"},
3926     {"on_move_out", c_on_move_out, "state=not_pressed"},
3927     {"on_key", c_on_key, "key=any"},
3928
3929     // control tags
3930  {"play", c_play, "name loop=0 @nomultiple=0"},
3931  {"stop", c_stop, "name= "},
3932  {"nextframe", c_nextframe, "name"},
3933  {"previousframe", c_previousframe, "name"},
3934
3935     // object placement tags
3936  {"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="},
3937  {"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="},
3938  {"move", c_move,       "name x= y= interpolation=linear"},
3939  {"smove", c_smove,     "name x= y= interpolation=linear"},
3940  {"sweep", c_sweep,     "name x= y= r= dir=counterclockwise arc=short interpolation=linear"},
3941  {"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"},
3942  //{"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3943  {"schange", c_schange, "name red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
3944  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
3945  {"del", c_del, "name"},
3946     // virtual object placement
3947  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
3948     // switching
3949  {"toggle", c_toggle, "name fixed_alignment="},
3950
3951     // commands which start a block
3952 //startclip (see above)
3953  {"sprite", c_sprite, "name"},
3954  {"action", c_action, "filename="},
3955  {"initaction", c_initaction, "name filename="},
3956
3957  {"end", c_end, ""}
3958 };
3959
3960
3961 static map_t parseArguments(char*command, char*pattern)
3962 {
3963     char*x;
3964     char*d,*e;
3965
3966     string_t name[64];
3967     string_t value[64];
3968     int set[64];
3969     int isboolean[64];
3970     int pos;
3971     int len;
3972     int t;
3973     string_t t1,t2;
3974     map_t result;
3975     map_init(&result);
3976
3977     string_set(&t1, "commandname");
3978     string_set(&t2, command);
3979     map_put(&result, t1, t2);
3980
3981     if(!pattern || !*pattern)
3982         return result;
3983
3984     x = pattern;
3985
3986     pos = 0;
3987
3988     if(!strncmp("<i> ", x, 3)) {
3989         readToken();
3990         if(type == COMMAND || type == RAWDATA) {
3991             pushBack();
3992             syntaxerror("character name expected");
3993         }
3994         name[pos].str = "instance";
3995         name[pos].len = 8;
3996         value[pos].str = text;
3997         value[pos].len = strlen(text);
3998         set[pos] = 1;
3999         pos++;
4000
4001         if(type == ASSIGNMENT)
4002             readToken();
4003
4004         name[pos].str = "character";
4005         name[pos].len = 9;
4006         value[pos].str = text;
4007         value[pos].len = strlen(text);
4008         set[pos] = 1;
4009         pos++;
4010
4011         x+=4;
4012     }
4013
4014     while(*x) {
4015         isboolean[pos] = (x[0] =='@');
4016         if(isboolean[pos])
4017             x++;
4018
4019         d = strchr(x, ' ');
4020         e = strchr(x, '=');
4021         if(!d)
4022             d=&x[strlen(x)];
4023         set[pos] = 0;
4024
4025         if(!e || d<e) {
4026             // no default
4027             name[pos].str = x;
4028             name[pos].len = d-x;
4029             value[pos].str = 0;
4030             value[pos].len = 0;
4031         } else {
4032             name[pos].str = x;
4033             name[pos].len = e-x;
4034             value[pos].str = e+1;
4035             value[pos].len = d-e-1;
4036         }
4037         pos++;
4038         if(!*d) break;
4039         x=d+1;
4040     }
4041     len = pos;
4042
4043 /*    for(t=0;t<len;t++) {
4044         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
4045                 isboolean[t]?"(boolean)":"");
4046     }*/
4047
4048     while(1) {
4049         readToken();
4050         if(type == RAWDATA || type == COMMAND) {
4051             pushBack();
4052             break;
4053         }
4054
4055         // first, search for boolean arguments
4056         for(pos=0;pos<len;pos++)
4057         {
4058             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
4059                 set[pos] = 1;
4060                 if(type == ASSIGNMENT)
4061                     readToken();
4062                 value[pos].str = text;
4063                 value[pos].len = strlen(text);
4064                 /*printf("setting boolean parameter %s (to %s)\n",
4065                         strdup_n(name[pos], namelen[pos]),
4066                         strdup_n(value[pos], valuelen[pos]));*/
4067                 break;
4068             }
4069         }
4070
4071         // second, search for normal arguments
4072         if(pos==len)
4073         for(pos=0;pos<len;pos++)
4074         {
4075             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
4076                (type != ASSIGNMENT && !set[pos])) {
4077                 if(set[pos]) {
4078                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
4079                 }
4080                 if(type == ASSIGNMENT)
4081                     readToken();
4082                 set[pos] = 1;
4083                 value[pos].str = text;
4084                 value[pos].len = strlen(text);
4085 #if 0//def DEBUG
4086                 printf("setting parameter %s (to %s)\n",
4087                         strdup_n(name[pos].str, name[pos].len),
4088                         strdup_n(value[pos].str, value[pos].len));
4089 #endif
4090                 break;
4091             }
4092         }
4093         if(pos==len) {
4094             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
4095         }
4096     }
4097 #if 0//def DEBUG
4098     for(t=0;t<len;t++) {
4099         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
4100     }
4101 #endif
4102     for(t=0;t<len;t++) {
4103         if(value[t].str && value[t].str[0] == '*') {
4104             //relative default- take value from some other parameter
4105             int s;
4106             for(s=0;s<len;s++) {
4107                 if(value[s].len == value[t].len-1 &&
4108                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
4109                     value[t].str = value[s].str;
4110             }
4111         }
4112         if(value[t].str == 0) {
4113             pushBack();
4114             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
4115         }
4116     }
4117
4118     /* ok, now construct the dictionary from the parameters */
4119
4120     for(t=0;t<len;t++)
4121     {
4122         map_put(&result, name[t], value[t]);
4123     }
4124     return result;
4125 }
4126 static void parseArgumentsForCommand(char*command)
4127 {
4128     int t;
4129     map_t args;
4130     int nr = -1;
4131     msg("<verbose> parse Command: %s (line %d)", command, line);
4132
4133     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
4134         if(!strcmp(arguments[t].command, command)) {
4135
4136             /* ugly hack- will be removed soon (once documentation and .sc generating
4137                utilities have been changed) */
4138             if(!strcmp(command, "swf") && !stackpos) {
4139                 warning("Please use .flash instead of .swf- this will be mandatory soon");
4140                 command = "flash";
4141                 t = 0;
4142             }
4143
4144             args = parseArguments(command, arguments[t].arguments);
4145             nr = t;
4146             break;
4147         }
4148     }
4149     if(nr<0)
4150         syntaxerror("command %s not known", command);
4151
4152     // catch missing .flash directives at the beginning of a file
4153     if(strcmp(command, "flash") && !stackpos)
4154     {
4155         syntaxerror("No movie defined- use .flash first");
4156     }
4157
4158 #ifdef DEBUG
4159     printf(".%s\n", command);fflush(stdout);
4160     map_dump(&args, stdout, "\t");fflush(stdout);
4161 #endif
4162
4163     (*arguments[nr].func)(&args);
4164
4165     /*if(!strcmp(command, "button") ||
4166        !strcmp(command, "action")) {
4167         while(1) {
4168             readToken();
4169             if(type == COMMAND) {
4170                 if(!strcmp(text, "end"))
4171                     break;
4172                 else {
4173                     pushBack();
4174                     break;
4175                 }
4176             }
4177         }
4178     }*/
4179
4180     map_clear(&args);
4181     return;
4182 }
4183
4184
4185 /* for now only intended to find what glyphs of each font are to be included in the swf file.
4186  * Therefore some knowledge about the command is assumed i.e. it is font/text-related
4187  * No syntax checking is done */
4188 static void analyseArgumentsForCommand(char*command)
4189 {
4190     int t;
4191     map_t args;
4192     char* fontfile;
4193     int nr = -1;
4194     msg("<verbose> analyse Command: %s (line %d)", command, line);
4195
4196     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++)
4197     {
4198         if(!strcmp(arguments[t].command, command))
4199         {
4200             args = parseArguments(command, arguments[t].arguments);
4201             nr = t;
4202             break;
4203         }
4204     }
4205 #ifdef DEBUG
4206     printf(".%s\n", command);fflush(stdout);
4207     map_dump(&args, stdout, "\t");fflush(stdout);
4208 #endif
4209     char* name = lu(&args, "name");
4210     if (!strcmp(command, "font"))
4211     {
4212         if(dictionary_lookup(&fonts, name))
4213             syntaxerror("font %s defined twice", name);
4214
4215         SWFFONT* font;
4216         fontfile = lu(&args, "filename");
4217         font = swf_LoadFont(fontfile);
4218         if(font == 0) {
4219             warning("Couldn't open font file \"%s\"", fontfile);
4220             font = (SWFFONT*)malloc(sizeof(SWFFONT));
4221             memset(font, 0, sizeof(SWFFONT));
4222         }
4223         swf_FontUseUTF8(font, lu(&args, "glyphs"));
4224         swf_FontPrepareForEditText(font);
4225         dictionary_put2(&fonts, name, font);
4226     }
4227     else
4228     {
4229         SWFFONT* font = dictionary_lookup(&fonts, lu(&args, "font"));
4230         if (!font)
4231             syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
4232         else
4233             if (!strcmp(command, "edittext"))
4234                 swf_FontUseAll(font);
4235             else
4236                 swf_FontUseUTF8(font, lu(&args, "text"));
4237     }
4238     map_clear(&args);
4239     return;
4240 }
4241
4242 void skipParameters()
4243 {
4244         do
4245                 readToken();
4246         while (type != COMMAND);
4247         pushBack();
4248 }
4249
4250 void findFontUsage()
4251 {
4252     char* fontRelated = "font;text;textshape;edittext;";
4253     while(!noMoreTokens())
4254     {
4255         readToken();
4256         if(type != COMMAND)
4257             syntaxerror("command expected");
4258         if (strstr(fontRelated, text))
4259             analyseArgumentsForCommand(text);
4260         else
4261             if(strcmp(text, "end"))
4262                 skipParameters();
4263     }
4264 }
4265
4266 void firstPass()
4267 {
4268     pos = 0;
4269     id = 0;
4270     dictionary_init(&fonts);
4271     cleanUp = &freeFontDictionary;
4272     findFontUsage();
4273 }
4274
4275 int main (int argc,char ** argv)
4276 {
4277     int t;
4278     processargs(argc, argv);
4279     initLog(0,-1,0,0,-1,verbose);
4280
4281     if(!filename) {
4282         args_callback_usage(argv[0]);
4283         exit(1);
4284     }
4285
4286     file = generateTokens(filename);
4287     if(!file) {
4288         fprintf(stderr, "parser returned error.\n");
4289         return 1;
4290     }
4291     firstPass();
4292     pos=0;
4293     t=0;
4294
4295     while(!noMoreTokens()) {
4296         readToken();
4297         if(type != COMMAND)
4298             syntaxerror("command expected");
4299         parseArgumentsForCommand(text);
4300     }
4301
4302     s_close();
4303     freeTokens(file);
4304
4305     return 0;
4306 }
4307