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