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