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