added defines, fixed segfault in startclip
[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     int add = 1;
2408     char*lastpos = str;
2409     while(*p) {
2410         if(*p == '+') 
2411             add = 1;
2412         else if(*p == '-')
2413             add = -1;
2414         else if(!lastpos)
2415             lastpos = p;
2416         p++;
2417         if((*p == '+' || *p == '-' || *p == 0) && lastpos) {
2418             char save = *p;
2419             *p = 0;
2420
2421             int l = 0;
2422             int v = 0;
2423             if(defines_initialized) {
2424                 l = (int)dictionary_lookup(&defines, lastpos);
2425             }
2426             if(l) {
2427                 v = *(int*)&define_values.buffer[l-1];
2428             } else {
2429                 v = parseRawTwip(lastpos);
2430             }
2431             *p = save;
2432             val += v*add;
2433             lastpos = 0;
2434             add = 1;
2435         }
2436     }
2437     return val;
2438 }
2439
2440 int parseArc(char* str)
2441 {
2442     if (!strcmp(str, "short"))
2443         return 1;
2444     if (!strcmp(str, "long"))
2445         return 0;
2446     syntaxerror("invalid value for the arc parameter: %s", str);
2447     return 1;
2448 }
2449
2450 int parseDir(char* str)
2451 {
2452     if (!strcmp(str, "clockwise"))
2453         return 1;
2454     if (!strcmp(str, "counterclockwise"))
2455         return 0;
2456     syntaxerror("invalid value for the dir parameter: %s", str);
2457     return 1;
2458 }
2459
2460 int isPoint(char*str)
2461 {
2462     if(strchr(str, '('))
2463         return 1;
2464     else
2465         return 0;
2466 }
2467
2468 SPOINT parsePoint(char*str)
2469 {
2470     SPOINT p;
2471     char tmp[80];
2472     int l = strlen(str);
2473     char*comma = strchr(str, ',');
2474     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
2475         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
2476     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
2477     p.x = parseTwip(tmp);
2478     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
2479     p.y = parseTwip(tmp);
2480     return p;
2481 }
2482
2483 int parseColor2(char*str, RGBA*color)
2484 {
2485     int l = strlen(str);
2486     int r,g,b,a;
2487     int t;
2488
2489     struct {unsigned char r,g,b;char*name;} colors[] =
2490     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
2491     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
2492     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
2493     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
2494     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
2495     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
2496     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
2497     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
2498     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
2499     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
2500     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
2501     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
2502
2503     a=255;r=g=b=0;
2504
2505     if(str[0]=='#' && (l==7 || l==9)) {
2506         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
2507             return 0;
2508         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
2509             return 0;
2510         color->r = r; color->g = g; color->b = b; color->a = a;
2511         return 1;
2512     }
2513     int len=strlen(str);
2514     U8 alpha = 255;
2515     if(strchr(str, '/')) {
2516         len = strchr(str, '/')-str;
2517         sscanf(str+len+1,"%02x", &alpha);
2518     }
2519     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
2520         if(!strncmp(str, colors[t].name, len)) {
2521             r = colors[t].r;
2522             g = colors[t].g;
2523             b = colors[t].b;
2524             a = alpha;
2525             color->r = r; color->g = g; color->b = b; color->a = a;
2526             return 1;
2527         }
2528     return 0;
2529
2530 }
2531 RGBA parseColor(char*str)
2532 {
2533     RGBA c;
2534     if(!parseColor2(str, &c))
2535         syntaxerror("Expression '%s' is not a color", str);
2536     return c;
2537 }
2538
2539 typedef struct _muladd {
2540     S16 mul;
2541     S16 add;
2542 } MULADD;
2543
2544 MULADD parseMulAdd(char*str)
2545 {
2546     float add, mul;
2547     char* str2 = (char*)malloc(strlen(str)+5);
2548     int i;
2549     MULADD m;
2550     strcpy(str2, str);
2551     strcat(str2, " 0");
2552     add = 0;
2553     mul = 1.0;
2554     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
2555     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
2556     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
2557     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
2558     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
2559     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
2560     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
2561     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
2562     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
2563     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
2564     else {
2565         syntaxerror("'%s' is not a valid color transform expression", str);
2566     }
2567     m.add = (int)(add*256);
2568     m.mul = (int)(mul*256);
2569     free(str2);
2570     return m;
2571 }
2572
2573 MULADD mergeMulAdd(MULADD m1, MULADD m2)
2574 {
2575     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
2576     double m = ((double)m1.mul*(double)m2.mul)/256.0;
2577     MULADD r;
2578     if(a<-32768) a=-32768;
2579     if(a>32767) a=32767;
2580     if(m<-32768) m=-32768;
2581     if(m>32767) m=32767;
2582     r.add = a;
2583     r.mul = (int)m;
2584     return r;
2585 }
2586
2587 float parsePxOrPercent(char*fontname, char*str)
2588 {
2589     int l = strlen(str);
2590     if(strchr(str, '%'))
2591         return parsePercent(str);
2592     if(l>2 && str[l-2]=='p' && str[l-1]=='t') {
2593         float p = atof(str);
2594         return p/64.0; /*64 = FT_SUBPIXELS- see ../lib/modules/swffont.c */
2595     }
2596     syntaxerror("Expression '%s' is neither a point size (?pt) nor a percentage (?%)", str);
2597     return 0;
2598 }
2599
2600 float parsePercent(char*str)
2601 {
2602     int l = strlen(str);
2603     if(!l)
2604         return 1.0;
2605     if(str[l-1]=='%') {
2606         return atoi(str)/100.0;
2607     }
2608     syntaxerror("Expression '%s' is not a percentage", str);
2609     return 0;
2610 }
2611 int isPercent(char*str)
2612 {
2613     return str[strlen(str)-1]=='%';
2614 }
2615 int parseNewSize(char*str, int size)
2616 {
2617     if(isPercent(str))
2618         return parsePercent(str)*size;
2619     else
2620         return (int)(atof(str)*20);
2621 }
2622
2623 int isColor(char*str)
2624 {
2625     RGBA c;
2626     return parseColor2(str, &c);
2627 }
2628
2629 static char* lu(map_t* args, char*name)
2630 {
2631     char* value = map_lookup(args, name);
2632     if(!value) {
2633         map_dump(args, stdout, "");
2634         syntaxerror("internal error 2: value %s should be set", name);
2635     }
2636     return value;
2637 }
2638
2639 static int c_flash(map_t*args)
2640 {
2641     char* filename = map_lookup(args, "filename");
2642     char* compressstr = lu(args, "compress");
2643     char* change_modestr = lu(args, "change-sets-all");
2644     char* exportstr = lu(args, "export");
2645     SRECT bbox = parseBox(lu(args, "bbox"));
2646     int version = parseInt(lu(args, "version"));
2647     int fps = (int)(parseFloat(lu(args, "fps"))*256);
2648     RGBA color = parseColor(lu(args, "background"));
2649     int compress = 0;
2650
2651     if(!filename || !*filename) {
2652         /* for compatibility */
2653         filename = map_lookup(args, "name");
2654         if(!filename || !*filename) {
2655             filename = 0;
2656         } else {
2657             //msg("<warning> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2658             msg("<notice> line %d: .flash name=... is deprecated, use .flash filename=...", line);
2659         }
2660     }
2661
2662     if(!filename || override_outputname)
2663         filename = outputname;
2664
2665     if(!strcmp(compressstr, "default"))
2666         compress = version>=6;
2667     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
2668         compress = 1;
2669     else if(!strcmp(compressstr, "no"))
2670         compress = 0;
2671     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
2672
2673     if(!strcmp(change_modestr, "yes"))
2674         change_sets_all = 1;
2675     else
2676         if(strcmp(change_modestr, "no"))
2677             syntaxerror("value \"%s\" not supported for the change-sets-all argument", change_modestr);
2678
2679     do_exports=atoi(exportstr);
2680
2681     s_swf(filename, bbox, version, fps, compress, color);
2682     return 0;
2683 }
2684 int isRelative(char*str)
2685 {
2686     return !strncmp(str, "<plus>", 6) ||
2687            !strncmp(str, "<minus>", 7);
2688 }
2689 char* getOffset(char*str)
2690 {
2691     if(!strncmp(str, "<plus>", 6))
2692         return str+6;
2693     if(!strncmp(str, "<minus>", 7))
2694         return str+7;
2695     syntaxerror("internal error (347)");
2696     return 0;
2697 }
2698 int getSign(char*str)
2699 {
2700     if(!strncmp(str, "<plus>", 6))
2701         return 1;
2702     if(!strncmp(str, "<minus>", 7))
2703         return -1;
2704     syntaxerror("internal error (348)");
2705     return 0;
2706 }
2707
2708 static dictionary_t points;
2709 static mem_t mpoints;
2710 static int points_initialized = 0;
2711
2712 static int c_interpolation(map_t *args)
2713 {
2714     int i;
2715     char* name = lu(args, "name");
2716     if (dictionary_lookup(&interpolations, name))
2717         syntaxerror("interpolation %s defined twice", name);
2718
2719     interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
2720     char* functionstr = lu(args, "function");
2721     inter->function = 0;
2722     for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
2723         if (!strcmp(functionstr,interpolationFunctions[i]))
2724         {
2725             inter->function = i + 1;
2726             break;
2727         }
2728     if (!inter->function)
2729         syntaxerror("unkown interpolation function %s", functionstr);
2730     inter->speed = parseFloat(lu(args, "speed"));
2731     inter->amplitude = parseTwip(lu(args, "amplitude"));
2732     inter->growth = parseFloat(lu(args, "growth"));
2733     inter->bounces = parseInt(lu(args, "bounces"));
2734     inter->damping = parseFloat(lu(args, "damping"));
2735     inter->slope = parseFloat(lu(args, "slope"));
2736
2737     dictionary_put2(&interpolations, name, inter);
2738     return 0;
2739 }
2740
2741 SPOINT getPoint(SRECT r, char*name)
2742 {
2743     int l=0;
2744     if(!strcmp(name, "center")) {
2745         SPOINT p;
2746         p.x = (r.xmin + r.xmax)/2;
2747         p.y = (r.ymin + r.ymax)/2;
2748         return p;
2749     }
2750     if (!strcmp(name, "bottom-center")) {
2751         SPOINT p;
2752         p.x = (r.xmin + r.xmax)/2;
2753         p.y = r.ymax;
2754         return p;
2755     }
2756     if (!strcmp(name, "top-center")) {
2757         SPOINT p;
2758         p.x = (r.xmin + r.xmax)/2;
2759         p.y = r.ymin;
2760         return p;
2761     }
2762     if (!strcmp(name, "top-left")) {
2763         SPOINT p;
2764         p.x = r.xmin;
2765         p.y = r.ymin;
2766         return p;
2767     }
2768     if (!strcmp(name, "top-right")) {
2769         SPOINT p;
2770         p.x = r.xmax;
2771         p.y = r.ymin;
2772         return p;
2773     }
2774     if (!strcmp(name, "bottom-right")) {
2775         SPOINT p;
2776         p.x = r.xmax;
2777         p.y = r.ymax;
2778         return p;
2779     }
2780     if (!strcmp(name, "bottom-left")) {
2781         SPOINT p;
2782         p.x = r.xmin;
2783         p.y = r.ymax;
2784         return p;
2785     }
2786     if (!strcmp(name, "left-center")) {
2787         SPOINT p;
2788         p.x = r.xmin;
2789         p.y = (r.ymin + r.ymax)/2;
2790         return p;
2791     }
2792     if (!strcmp(name, "right-center")) {
2793         SPOINT p;
2794         p.x = r.xmax;
2795         p.y = (r.ymin + r.ymax)/2;
2796         return p;
2797     }
2798
2799
2800     if(points_initialized)
2801         l = (int)dictionary_lookup(&points, name);
2802     if(l==0) {
2803         syntaxerror("Invalid point: \"%s\".", name);
2804     }
2805     return *(SPOINT*)&mpoints.buffer[l-1];
2806 }
2807
2808
2809 static int texture2(char*name, char*object, map_t*args, int errors)
2810 {
2811     SPOINT pos,size;
2812     char*xstr = map_lookup(args, "x");
2813     char*ystr = map_lookup(args, "y");
2814     char*widthstr = map_lookup(args, "width");
2815     char*heightstr = map_lookup(args, "height");
2816     char*scalestr = map_lookup(args, "scale");
2817     char*scalexstr = map_lookup(args, "scalex");
2818     char*scaleystr = map_lookup(args, "scaley");
2819     char*rotatestr = map_lookup(args, "rotate");
2820     char* shearstr = map_lookup(args, "shear");
2821     char* radiusstr = map_lookup(args, "r");
2822     float x=0,y=0;
2823     float scalex = 1.0, scaley = 1.0;
2824     float rotate=0, shear=0;
2825     float r = 0;
2826     if(!*xstr && !*ystr) {
2827         if(errors)
2828             syntaxerror("x and y must be set");
2829         return 0;
2830     }
2831     if(*scalestr && (*scalexstr || *scaleystr)) {
2832         syntaxerror("scale and scalex/scaley can't both be set");
2833         return 0;
2834     }
2835     if((*widthstr || *heightstr) && *radiusstr) {
2836         syntaxerror("width/height and radius can't both be set");
2837     }
2838     if(*radiusstr) {
2839         widthstr = radiusstr;
2840         heightstr = radiusstr;
2841     }
2842     if(!*xstr) xstr="0";
2843     if(!*ystr) ystr="0";
2844     if(!*rotatestr) rotatestr="0";
2845     if(!*shearstr) shearstr="0";
2846
2847     if(*scalestr) {
2848         scalex = scaley = parsePercent(scalestr);
2849     } else if(*scalexstr || *scaleystr) {
2850         if(scalexstr) scalex = parsePercent(scalexstr);
2851         if(scaleystr) scaley = parsePercent(scaleystr);
2852     } else if(*widthstr || *heightstr) {
2853         int width=0;
2854         int height=0;
2855         s_getBitmapSize(object, &width, &height);
2856         if(*widthstr)
2857             scalex = (float)parseTwip(widthstr)/(float)width;
2858         if(*heightstr)
2859             scaley = (float)parseTwip(heightstr)/(float)height;
2860     }
2861     x = parseTwip(xstr);
2862     y = parseTwip(ystr);
2863     rotate = parseFloat(rotatestr);
2864     shear = parseFloat(shearstr);
2865
2866     s_texture(name, object, x,y,scalex,scaley,rotate, shear);
2867
2868     return 0;
2869 }
2870
2871 static int c_texture(map_t*args)
2872 {
2873     char*name = lu(args, "instance");
2874     char*object = lu(args, "character");
2875     return texture2(name, object, args, 1);
2876 }
2877
2878 static int c_gradient(map_t*args)
2879 {
2880     char*name = lu(args, "name");
2881     int radial= strcmp(lu(args, "radial"), "radial")?0:1;
2882     int rotate = parseInt(lu(args, "rotate"));
2883
2884     readToken();
2885     if(type != RAWDATA)
2886         syntaxerror("colon (:) expected");
2887
2888     if(dictionary_lookup(&gradients, name))
2889         syntaxerror("gradient %s defined twice", name);
2890
2891     s_gradient(name, text, radial, rotate);
2892
2893     /* check whether we also have placement information,
2894        which would make this a positioned gradient.
2895        If there is placement information, texture2() will
2896        add a texture, which has priority over the gradient.
2897      */
2898     texture2(name, name, args, 0);
2899     return 0;
2900 }
2901
2902 static char* checkFiltername(map_t* args)
2903 {
2904     char* name = lu(args, "name");
2905     if (strchr(name, ','))
2906         syntaxerror("the comma (,) is used to separate filters in filterlists. Please do not use in filternames.");
2907     return name;
2908 }
2909
2910 static int c_blur(map_t*args)
2911 {
2912     char*name = checkFiltername(args);
2913     char*blurstr = lu(args, "blur");
2914     char*blurxstr = lu(args, "blurx");
2915     char*blurystr = lu(args, "blury");
2916     float blurx=1.0, blury=1.0;
2917     if(blurstr[0]) {
2918         blurx = parseFloat(blurstr);
2919         blury = parseFloat(blurstr);
2920     }
2921     if(blurxstr[0])
2922         blurx = parseFloat(blurxstr);
2923     if(blurystr[0])
2924         blury = parseFloat(blurystr);
2925     int passes = parseInt(lu(args, "passes"));
2926     s_blur(name, blurx, blury, passes);
2927     return 0;
2928 }
2929
2930 static int c_gradientglow(map_t*args)
2931 {
2932     char*name = checkFiltername(args);
2933     char*gradient = lu(args, "gradient");
2934     char*blurstr = lu(args, "blur");
2935     char*blurxstr = lu(args, "blurx");
2936     char*blurystr = lu(args, "blury");
2937     float blurx=1.0, blury=1.0;
2938     if(blurstr[0]) {
2939         blurx = parseFloat(blurstr);
2940         blury = parseFloat(blurstr);
2941     }
2942     if(blurxstr[0])
2943         blurx = parseFloat(blurxstr);
2944     if(blurystr[0])
2945         blury = parseFloat(blurystr);
2946
2947     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2948     float distance = parseFloat(lu(args, "distance"));
2949     float strength = parseFloat(lu(args, "strength"));
2950     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2951     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2952     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2953     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
2954     int passes = parseInt(lu(args, "passes"));
2955
2956     s_gradientglow(name, gradient, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
2957     return 0;
2958 }
2959
2960 static int c_dropshadow(map_t*args)
2961 {
2962     char*name = checkFiltername(args);
2963     RGBA color = parseColor(lu(args, "color"));
2964     char*blurstr = lu(args, "blur");
2965     char*blurxstr = lu(args, "blurx");
2966     char*blurystr = lu(args, "blury");
2967     float blurx=1.0, blury=1.0;
2968     if(blurstr[0]) {
2969         blurx = parseFloat(blurstr);
2970         blury = parseFloat(blurstr);
2971     }
2972     if(blurxstr[0])
2973         blurx = parseFloat(blurxstr);
2974     if(blurystr[0])
2975         blury = parseFloat(blurystr);
2976
2977     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
2978     float distance = parseFloat(lu(args, "distance"));
2979     float strength = parseFloat(lu(args, "strength"));
2980     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
2981     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
2982     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
2983     int passes = parseInt(lu(args, "passes"));
2984
2985     s_dropshadow(name, color, blurx, blury, angle, distance, strength, innershadow, knockout, composite, passes);
2986     return 0;
2987 }
2988
2989 static int c_bevel(map_t*args)
2990 {
2991     char*name = checkFiltername(args);
2992     RGBA shadow = parseColor(lu(args, "shadow"));
2993     RGBA highlight = parseColor(lu(args, "highlight"));
2994     char*blurstr = lu(args, "blur");
2995     char*blurxstr = lu(args, "blurx");
2996     char*blurystr = lu(args, "blury");
2997     float blurx=1.0, blury=1.0;
2998     if(blurstr[0]) {
2999         blurx = parseFloat(blurstr);
3000         blury = parseFloat(blurstr);
3001     }
3002     if(blurxstr[0])
3003         blurx = parseFloat(blurxstr);
3004     if(blurystr[0])
3005         blury = parseFloat(blurystr);
3006
3007     float angle = parseFloat(lu(args, "angle")) / 180 * M_PI;
3008     float distance = parseFloat(lu(args, "distance"));
3009     float strength = parseFloat(lu(args, "strength"));
3010     char innershadow = strcmp(lu(args, "innershadow"),"innershadow")?0:1;
3011     char knockout = strcmp(lu(args, "knockout"),"knockout")?0:1;
3012     char composite = strcmp(lu(args, "composite"),"composite")?0:1;
3013     char ontop = strcmp(lu(args, "ontop"),"ontop")?0:1;
3014     int passes = parseInt(lu(args, "passes"));
3015
3016     s_bevel(name, shadow, highlight, blurx, blury, angle, distance, strength, innershadow, knockout, composite, ontop, passes);
3017     return 0;
3018 }
3019
3020 static int c_define(map_t*args)
3021 {
3022     char*name = lu(args, "name");
3023     char*value = lu(args, "value");
3024     
3025     if(!defines_initialized) {
3026         dictionary_init(&defines);
3027         mem_init(&define_values);
3028         defines_initialized = 1;
3029     }
3030     int val = parseTwip(value);
3031     int pos = mem_put(&define_values, &val, sizeof(val));
3032     string_t s;
3033     string_set(&s, name);
3034     dictionary_put(&defines, s, (void*)(pos+1));
3035     return 0;
3036 }
3037 static int c_point(map_t*args)
3038 {
3039     char*name = lu(args, "name");
3040     int pos;
3041     string_t s1;
3042     SPOINT p;
3043     if(!points_initialized) {
3044         dictionary_init(&points);
3045         mem_init(&mpoints);
3046         points_initialized = 1;
3047     }
3048     p.x = parseTwip(lu(args, "x"));
3049     p.y = parseTwip(lu(args, "y"));
3050     pos = mem_put(&mpoints, &p, sizeof(p));
3051     string_set(&s1, name);
3052     dictionary_put(&points, s1, (void*)(pos+1));
3053     return 0;
3054 }
3055 static int c_play(map_t*args)
3056 {
3057     char*name = lu(args, "name");
3058     char*loop = lu(args, "loop");
3059     char*nomultiple = lu(args, "nomultiple");
3060     int nm = 0;
3061     if(!strcmp(nomultiple, "nomultiple"))
3062         nm = 1;
3063     else
3064         nm = parseInt(nomultiple);
3065
3066     if(s_playsound(name, parseInt(loop), nm, 0)) {
3067         return 0;
3068     } else if(s_swf3action(name, "play")) {
3069         return 0;
3070     }
3071     return 0;
3072 }
3073
3074 static int c_stop(map_t*args)
3075 {
3076     char*name = map_lookup(args, "name");
3077
3078     if(s_playsound(name, 0,0,1))
3079         return 0;
3080     else if(s_swf3action(name, "stop"))
3081         return 0;
3082     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
3083         return 0;
3084 }
3085
3086 static int c_nextframe(map_t*args)
3087 {
3088     char*name = lu(args, "name");
3089
3090     if(s_swf3action(name, "nextframe")) {
3091         return 0;
3092     }
3093     syntaxerror("I don't know anything about movie \"%s\"", name);
3094     return 0;
3095 }
3096
3097 static int c_previousframe(map_t*args)
3098 {
3099     char*name = lu(args, "name");
3100
3101     if(s_swf3action(name, "previousframe")) {
3102         return 0;
3103     }
3104     syntaxerror("I don't know anything about movie \"%s\"", name);
3105     return 0;
3106 }
3107
3108 static int c_movement(map_t*args, int type)
3109 {
3110     char*instance = lu(args, "name");
3111
3112     char* xstr="";
3113     char* ystr="";
3114     SRECT oldbbox;
3115     parameters_t p;
3116     U16 set = 0x0000;
3117
3118     xstr = lu(args, "x");
3119     ystr = lu(args, "y");
3120
3121     s_getParameters(instance, &p);
3122
3123     /* x,y position */
3124     if(xstr[0])
3125     {
3126         if(isRelative(xstr))
3127         {
3128             if(type == PT_PUT || type == PT_STARTCLIP)
3129                 syntaxerror("relative x values not allowed for initial put or startclip");
3130             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3131         }
3132         else
3133         {
3134             p.x = parseTwip(xstr);
3135         }
3136         set = set | SF_X;
3137      }
3138     if(ystr[0])
3139     {
3140         if(isRelative(ystr))
3141         {
3142             if(type == PT_PUT || type == PT_STARTCLIP)
3143                 syntaxerror("relative y values not allowed for initial put or startclip");
3144             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3145         }
3146         else
3147         {
3148             p.y = parseTwip(ystr);
3149         }
3150         set = set | SF_Y;
3151     }
3152
3153     if (change_sets_all)
3154         set = SF_ALL;
3155     p.set = set;
3156
3157     switch (type)
3158     {
3159         case PT_MOVE:
3160             {
3161                 char* interstr = lu(args, "interpolation");
3162                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3163                 if (!inter)
3164                     syntaxerror("unkown interpolation %s", interstr);
3165                 s_change(instance, p, inter);
3166             }
3167             break;
3168         case PT_SMOVE:
3169             {
3170                 char* interstr = lu(args, "interpolation");
3171                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3172                 if (!inter)
3173                     syntaxerror("unkown interpolation %s", interstr);
3174                 s_schange(instance, p, inter);
3175             }
3176             break;
3177         case PT_SWEEP:
3178             {
3179                 char* rstr = lu(args, "r");
3180                 int radius = parseTwip(rstr);
3181                 if (radius <= 0)
3182                         syntaxerror("sweep not possible: radius must be greater than 0.");
3183                 char* dirstr = lu(args, "dir");
3184                 int clockwise = parseDir(dirstr);
3185                 char* arcstr = lu(args, "arc");
3186                 int short_arc = parseArc(arcstr);
3187                 char* interstr = lu(args, "interpolation");
3188                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3189                 if (!inter)
3190                     syntaxerror("unkown interpolation %s", interstr);
3191                 s_sweep(instance, p, radius, clockwise, short_arc, inter);
3192             }
3193             break;
3194         }
3195     return 0;
3196 }
3197
3198 static int c_placement(map_t*args, int type)
3199 {
3200     char*instance = lu(args, (type==PT_PUT||type==PT_STARTCLIP)?"instance":"name");
3201     char*character = 0;
3202
3203     char* luminancestr = lu(args, "luminance");
3204     char* scalestr = lu(args, "scale");
3205     char* scalexstr = lu(args, "scalex");
3206     char* scaleystr = lu(args, "scaley");
3207     char* rotatestr = lu(args, "rotate");
3208     char* shearstr = lu(args, "shear");
3209     char* xstr="", *pivotstr="";
3210     char* ystr="", *anglestr="";
3211     char*above = lu(args, "above"); /*FIXME*/
3212     char*below = lu(args, "below");
3213     char* rstr = lu(args, "red");
3214     char* gstr = lu(args, "green");
3215     char* bstr = lu(args, "blue");
3216     char* astr = lu(args, "alpha");
3217     char* pinstr = lu(args, "pin");
3218     char* as = map_lookup(args, "as");
3219     char* blendmode = lu(args, "blend");
3220     char* filterstr = lu(args, "filter");
3221     U8 blend;
3222     MULADD r,g,b,a;
3223     float oldwidth;
3224     float oldheight;
3225     SRECT oldbbox;
3226     MULADD luminance;
3227     parameters_t p;
3228     U16 set = 0x0000;
3229
3230     if(type==9)
3231     { // (?) .rotate  or .arcchange
3232         pivotstr = lu(args, "pivot");
3233         anglestr = lu(args, "angle");
3234     }
3235     else
3236     {
3237         xstr = lu(args, "x");
3238         ystr = lu(args, "y");
3239     }
3240
3241     if(luminancestr[0])
3242         luminance = parseMulAdd(luminancestr);
3243     else
3244     {
3245         luminance.add = 0;
3246         luminance.mul = 256;
3247     }
3248
3249     if(scalestr[0])
3250     {
3251         if(scalexstr[0]||scaleystr[0])
3252             syntaxerror("scalex/scaley and scale cannot both be set");
3253         scalexstr = scaleystr = scalestr;
3254     }
3255
3256     if(type == PT_PUT || type == PT_STARTCLIP)  {
3257         // put or startclip
3258         character = lu(args, "character");
3259         parameters_clear(&p);
3260     } else if (type == PT_BUTTON) {
3261         character = lu(args, "name");
3262         parameters_clear(&p);
3263         // button's show
3264     } else {
3265         s_getParameters(instance, &p);
3266     }
3267
3268     /* x,y position */
3269     if(xstr[0])
3270     {
3271         if(isRelative(xstr))
3272         {
3273             if(type == PT_PUT || type == PT_STARTCLIP)
3274                 syntaxerror("relative x values not allowed for initial put or startclip");
3275             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
3276         }
3277         else
3278         {
3279             p.x = parseTwip(xstr);
3280         }
3281         set = set | SF_X;
3282      }
3283     if(ystr[0])
3284     {
3285         if(isRelative(ystr))
3286         {
3287             if(type == PT_PUT || type == PT_STARTCLIP)
3288                 syntaxerror("relative y values not allowed for initial put or startclip");
3289             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
3290         }
3291         else
3292         {
3293             p.y = parseTwip(ystr);
3294         }
3295         set = set | SF_Y;
3296         }
3297
3298     /* scale, scalex, scaley */
3299     if(character)
3300         oldbbox = s_getCharBBox(character);
3301     else
3302         oldbbox = s_getInstanceBBox(instance);
3303     oldwidth = oldbbox.xmax - oldbbox.xmin;
3304     oldheight = oldbbox.ymax - oldbbox.ymin;
3305     if(scalexstr[0])
3306     {
3307         if(oldwidth==0)
3308             p.scalex = 1.0;
3309         else
3310             if(scalexstr[0])
3311                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
3312         set = set | SF_SCALEX;
3313     }
3314    if(scaleystr[0])
3315    {
3316         if(oldheight==0)
3317             p.scaley = 1.0;
3318         else
3319             if(scaleystr[0])
3320                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
3321         set = set | SF_SCALEY;
3322    }
3323
3324     /* rotation */
3325     if(rotatestr[0])
3326     {
3327         if(isRelative(rotatestr))
3328             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
3329         else
3330             p.rotate = parseFloat(rotatestr);
3331         set = set | SF_ROTATE;
3332         }
3333
3334     /* shearing */
3335     if(shearstr[0])
3336     {
3337         if(isRelative(shearstr))
3338             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
3339         else
3340             p.shear = parseFloat(shearstr);
3341         set = set | SF_SHEAR;
3342     }
3343
3344     if(pivotstr[0])
3345     {
3346         if(isPoint(pivotstr))
3347             p.pivot = parsePoint(pivotstr);
3348         else
3349             p.pivot = getPoint(oldbbox, pivotstr);
3350         set = set | SF_PIVOT;
3351     }
3352
3353     if(pinstr[0])
3354     {
3355         if(isPoint(pinstr))
3356             p.pin = parsePoint(pinstr);
3357         else
3358             p.pin = getPoint(oldbbox, pinstr);
3359         set = set | SF_PIN;
3360     }
3361
3362     /* color transform */
3363
3364     if(rstr[0] || luminancestr[0])
3365     {
3366         MULADD r;
3367         if(rstr[0])
3368             r = parseMulAdd(rstr);
3369         else
3370         {
3371             r.add = p.cxform.r0;
3372             r.mul = p.cxform.r1;
3373         }
3374         r = mergeMulAdd(r, luminance);
3375         p.cxform.r0 = r.mul;
3376         p.cxform.r1 = r.add;
3377         set = set | SF_CX_R;
3378         }
3379     if(gstr[0] || luminancestr[0])
3380     {
3381         MULADD g;
3382         if(gstr[0])
3383             g = parseMulAdd(gstr);
3384         else
3385         {
3386             g.add = p.cxform.g0;
3387             g.mul = p.cxform.g1;
3388         }
3389         g = mergeMulAdd(g, luminance);
3390         p.cxform.g0 = g.mul;
3391         p.cxform.g1 = g.add;
3392         set = set | SF_CX_G;
3393     }
3394     if(bstr[0] || luminancestr[0])
3395     {
3396         MULADD b;
3397         if(bstr[0])
3398             b = parseMulAdd(bstr);
3399         else
3400         {
3401             b.add = p.cxform.b0;
3402             b.mul = p.cxform.b1;
3403         }
3404         b = mergeMulAdd(b, luminance);
3405         p.cxform.b0 = b.mul;
3406         p.cxform.b1 = b.add;
3407         set = set | SF_CX_B;
3408     }
3409     if(astr[0])
3410     {
3411         MULADD a = parseMulAdd(astr);
3412         p.cxform.a0 = a.mul;
3413         p.cxform.a1 = a.add;
3414         set = set | SF_CX_A;
3415     }
3416
3417     if(blendmode[0])
3418     {
3419         int t;
3420         blend = 255;
3421         for(t = 0; blendModeNames[t]; t++)
3422         {
3423             if(!strcmp(blendModeNames[t], blendmode))
3424             {
3425                 blend = t;
3426                 break;
3427             }
3428         }
3429         if(blend == 255)
3430         {
3431             syntaxerror("unknown blend mode: '%s'", blendmode);
3432         }
3433         p.blendmode = blend;
3434         set = set | SF_BLEND;
3435         }
3436
3437     if(filterstr[0])
3438     {
3439         p.filters = parseFilters(filterstr);
3440         set = set | SF_FILTER;
3441     }
3442
3443     if (type == PT_CHANGE && set & (SF_X | SF_Y))
3444         warning("As of version 0.8.2 using the .change command to modify an \
3445 object's position on the stage is considered deprecated. Future \
3446 versions may consider x and y parameters for the .change command \
3447 to be illegal; please use the .move command.");
3448
3449     if (change_sets_all)
3450         set = SF_ALL;
3451     p.set = set;
3452
3453     switch (type)
3454     {
3455         case PT_PUT:
3456             s_put(instance, character, p);
3457             break;
3458         case PT_CHANGE:
3459             {
3460                 char* interstr = lu(args, "interpolation");
3461                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3462                 if (!inter)
3463                     syntaxerror("unkown interpolation %s", interstr);
3464                 s_change(instance, p, inter);
3465             }
3466             break;
3467         case PT_SCHANGE:
3468             {
3469                 char* interstr = lu(args, "interpolation");
3470                 interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
3471                 if (!inter)
3472                     syntaxerror("unkown interpolation %s", interstr);
3473                 s_schange(instance, p, inter);
3474             }
3475             break;
3476         case PT_JUMP:
3477             s_jump(instance, p);
3478             break;
3479         case PT_STARTCLIP:
3480             s_startclip(instance, character, p);
3481             break;
3482         case PT_BUTTON:
3483             if(as && as[0])
3484                 s_buttonput(character, as, p);
3485             else
3486                 s_buttonput(character, "shape", p);
3487             break;
3488 //        default:
3489         }
3490     return 0;
3491 }
3492 static int c_put(map_t*args)
3493 {
3494     c_placement(args, PT_PUT);
3495     return 0;
3496 }
3497 static int c_change(map_t*args)
3498 {
3499     if (currentframe == 0)
3500         warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
3501     c_placement(args, PT_CHANGE);
3502     return 0;
3503 }
3504 static int c_schange(map_t*args)
3505 {
3506     c_placement(args, PT_SCHANGE);
3507     return 0;
3508 }
3509 static int c_move(map_t* args)
3510 {
3511     c_movement(args, PT_MOVE);
3512     return 0;
3513 }
3514 static int c_smove(map_t* args)
3515 {
3516     c_movement(args, PT_SMOVE);
3517     return 0;
3518 }
3519 static int c_sweep(map_t* args)
3520 {
3521     c_movement(args, PT_SWEEP);
3522     return 0;
3523 }
3524 static int c_arcchange(map_t*args)
3525 {
3526     c_placement(args, 0);
3527     return 0;
3528 }
3529 static int c_jump(map_t*args)
3530 {
3531     c_placement(args, PT_JUMP);
3532     return 0;
3533 }
3534 static int c_startclip(map_t*args)
3535 {
3536     c_placement(args, PT_STARTCLIP);
3537     return 0;
3538 }
3539 static int c_show(map_t*args)
3540 {
3541     c_placement(args, PT_BUTTON);
3542     return 0;
3543 }
3544 static int c_toggle(map_t* args)
3545 {
3546     char*instance = lu(args, "name");
3547     U16 flagsOn = 0x0000, flagsOff = 0xffff;
3548     char* alignstr = lu(args, "fixed_alignment");
3549     if (!strcmp(alignstr, "on"))
3550         flagsOn += IF_FIXED_ALIGNMENT;
3551     else
3552         if (!strcmp(alignstr, "off"))
3553             flagsOff -= IF_FIXED_ALIGNMENT;
3554         else
3555             syntaxerror("values for toggle must be \"on\" or \"off\". %s is not legal.", alignstr);
3556     s_toggle(instance, flagsOn, flagsOff);
3557     return 0;
3558 }
3559 static int c_del(map_t*args)
3560 {
3561     char*instance = lu(args, "name");
3562     s_delinstance(instance);
3563     return 0;
3564 }
3565 static int c_end(map_t*args)
3566 {
3567     s_end();
3568     return 0;
3569 }
3570 static int c_sprite(map_t*args)
3571 {
3572     char* name = lu(args, "name");
3573     char* scalinggrid = lu(args, "scalinggrid");
3574
3575     if(scalinggrid && *scalinggrid) {
3576         SRECT r = parseBox(scalinggrid);
3577         s_sprite(name, &r);
3578     } else {
3579         s_sprite(name, 0);
3580     }
3581     return 0;
3582 }
3583 static int c_frame(map_t*args)
3584 {
3585     char*framestr = lu(args, "n");
3586     char*cutstr = lu(args, "cut");
3587
3588     char*name = lu(args, "name");
3589     char*anchor = lu(args, "anchor");
3590     char buf[40];
3591
3592     if(!strcmp(anchor, "anchor") && !*name)
3593         name = framestr;
3594
3595     int frame;
3596     int cut = 0;
3597     if(strcmp(cutstr, "no"))
3598         cut = 1;
3599     if(isRelative(framestr)) {
3600         frame = s_getframe();
3601         if(getSign(framestr)<0)
3602             syntaxerror("relative frame expressions must be positive");
3603         frame += parseInt(getOffset(framestr));
3604     }
3605     else {
3606         frame = parseInt(framestr);
3607         if(s_getframe() >= frame
3608                 && !(frame==1 && s_getframe()==frame)) // equality is o.k. for frame 0
3609             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
3610     }
3611     s_frame(frame, cut, name, !strcmp(anchor, "anchor"));
3612     return 0;
3613 }
3614 static int c_primitive(map_t*args)
3615 {
3616     char*name = lu(args, "name");
3617     char*command = lu(args, "commandname");
3618     int width=0, height=0, r=0;
3619     int linewidth = parseTwip(lu(args, "line"));
3620     char*colorstr = lu(args, "color");
3621     RGBA color = parseColor(colorstr);
3622     char*fillstr = lu(args, "fill");
3623     int dofill = 1;
3624     int type=0;
3625     char* font;
3626     char* text;
3627     char* outline=0;
3628     RGBA fill;
3629     if(!strcmp(command, "circle"))
3630         type = 1;
3631     else if(!strcmp(command, "filled"))
3632         type = 2;
3633
3634     if(type==0) {
3635         width = parseTwip(lu(args, "width"));
3636         height = parseTwip(lu(args, "height"));
3637     } else if (type==1) {
3638         r = parseTwip(lu(args, "r"));
3639     } else if (type==2) {
3640         outline = lu(args, "outline");
3641     }
3642
3643     if(!strcmp(fillstr, "fill"))
3644         fillstr = colorstr;
3645     if(!strcmp(fillstr, "none"))
3646         fillstr = 0;
3647     if(width<0 || height<0 || linewidth<0 || r<0)
3648         syntaxerror("values width, height, line, r must be positive");
3649
3650     if(type == 0) s_box(name, width, height, color, linewidth, fillstr);
3651     else if(type==1) s_circle(name, r, color, linewidth, fillstr);
3652     else if(type==2) s_filled(name, outline, color, linewidth, fillstr);
3653     return 0;
3654 }
3655
3656 static int c_textshape(map_t*args)
3657 {
3658     char*name = lu(args, "name");
3659     char*text = lu(args, "text");
3660     char*font = lu(args, "font");
3661     float size = parsePxOrPercent(font, lu(args, "size"));
3662
3663     s_textshape(name, font, size, text);
3664     return 0;
3665 }
3666
3667 static int c_swf(map_t*args)
3668 {
3669     char*name = lu(args, "name");
3670     char*filename = lu(args, "filename");
3671     char*command = lu(args, "commandname");
3672     if(!strcmp(command, "shape"))
3673         warning("Please use .swf instead of .shape");
3674     s_includeswf(name, filename);
3675     return 0;
3676 }
3677
3678 static int c_font(map_t*args)
3679 {
3680     char*name = lu(args, "name");
3681     char*filename = lu(args, "filename");
3682     s_font(name, filename);
3683     return 0;
3684 }
3685
3686 static int c_sound(map_t*args)
3687 {
3688     char*name = lu(args, "name");
3689     char*filename = lu(args, "filename");
3690     s_sound(name, filename);
3691     return 0;
3692 }
3693
3694 static int c_text(map_t*args)
3695 {
3696     char*name = lu(args, "name");
3697     char*text = lu(args, "text");
3698     char*font = lu(args, "font");
3699     float size = parsePxOrPercent(font, lu(args, "size"));
3700     RGBA color = parseColor(lu(args, "color"));
3701     s_text(name, font, text, (int)(size*100), color);
3702     return 0;
3703 }
3704
3705 static int c_soundtrack(map_t*args)
3706 {
3707     return 0;
3708 }
3709
3710 static int c_quicktime(map_t*args)
3711 {
3712     char*name = lu(args, "name");
3713     char*url = lu(args, "url");
3714     s_quicktime(name, url);
3715     return 0;
3716 }
3717
3718 static int c_image(map_t*args)
3719 {
3720     char*command = lu(args, "commandname");
3721     char*name = lu(args, "name");
3722     char*filename = lu(args, "filename");
3723     if(!strcmp(command,"jpeg")) {
3724         int quality = (int)(parsePercent(lu(args, "quality"))*100);
3725         s_image(name, "jpeg", filename, quality);
3726     } else {
3727         s_image(name, "png", filename, 0);
3728     }
3729     return 0;
3730 }
3731
3732 static int c_outline(map_t*args)
3733 {
3734     char*name = lu(args, "name");
3735     char*format = lu(args, "format");
3736
3737     readToken();
3738     if(type != RAWDATA)
3739         syntaxerror("colon (:) expected");
3740
3741     s_outline(name, format, text);
3742     return 0;
3743 }
3744
3745 int fakechar(map_t*args)
3746 {
3747     char*name = lu(args, "name");
3748     s_box(name, 0, 0, black, 20, 0);
3749     return 0;
3750 }
3751
3752 static int c_egon(map_t*args) {return fakechar(args);}
3753 static int c_button(map_t*args) {
3754     char*name = lu(args, "name");
3755     s_button(name);
3756     return 0;
3757 }
3758 static int current_button_flags = 0;
3759 static int c_on_press(map_t*args)
3760 {
3761     char*position = lu(args, "position");
3762     char*action = "";
3763     if(!strcmp(position, "inside")) {
3764         current_button_flags |= BC_OVERUP_OVERDOWN;
3765     } else if(!strcmp(position, "outside")) {
3766         //current_button_flags |= BC_IDLE_OUTDOWN;
3767         syntaxerror("IDLE_OVERDOWN not supported by SWF");
3768     } else if(!strcmp(position, "anywhere")) {
3769         current_button_flags |= /*BC_IDLE_OUTDOWN|*/BC_OVERUP_OVERDOWN|BC_IDLE_OVERDOWN;
3770     }
3771     readToken();
3772     if(type == RAWDATA) {
3773         action = text;
3774         s_buttonaction(current_button_flags, action);
3775         current_button_flags = 0;
3776     }
3777     else
3778         pushBack();
3779     return 0;
3780 }
3781 static int c_on_release(map_t*args)
3782 {
3783     char*position = lu(args, "position");
3784     char*action = "";
3785     if(!strcmp(position, "inside")) {
3786         current_button_flags |= BC_OVERDOWN_OVERUP;
3787     } else if(!strcmp(position, "outside")) {
3788         current_button_flags |= BC_OUTDOWN_IDLE;
3789     } else if(!strcmp(position, "anywhere")) {
3790         current_button_flags |= BC_OVERDOWN_OVERUP|BC_OUTDOWN_IDLE|BC_OVERDOWN_IDLE;
3791     }
3792     readToken();
3793     if(type == RAWDATA) {
3794         action = text;
3795         s_buttonaction(current_button_flags, action);
3796         current_button_flags = 0;
3797     }
3798     else
3799         pushBack();
3800     return 0;
3801 }
3802 static int c_on_move_in(map_t*args)
3803 {
3804     char*position = lu(args, "state");
3805     char*action = "";
3806     if(!strcmp(position, "pressed")) {
3807         current_button_flags |= BC_OUTDOWN_OVERDOWN;
3808     } else if(!strcmp(position, "not_pressed")) {
3809         current_button_flags |= BC_IDLE_OVERUP;
3810     } else if(!strcmp(position, "any")) {
3811         current_button_flags |= BC_OUTDOWN_OVERDOWN|BC_IDLE_OVERUP|BC_IDLE_OVERDOWN;
3812     }
3813     readToken();
3814     if(type == RAWDATA) {
3815         action = text;
3816         s_buttonaction(current_button_flags, action);
3817         current_button_flags = 0;
3818     }
3819     else
3820         pushBack();
3821     return 0;
3822 }
3823 static int c_on_move_out(map_t*args)
3824 {
3825     char*position = lu(args, "state");
3826     char*action = "";
3827     if(!strcmp(position, "pressed")) {
3828         current_button_flags |= BC_OVERDOWN_OUTDOWN;
3829     } else if(!strcmp(position, "not_pressed")) {
3830         current_button_flags |= BC_OVERUP_IDLE;
3831     } else if(!strcmp(position, "any")) {
3832         current_button_flags |= BC_OVERDOWN_OUTDOWN|BC_OVERUP_IDLE|BC_OVERDOWN_IDLE;
3833     }
3834     readToken();
3835     if(type == RAWDATA) {
3836         action = text;
3837         s_buttonaction(current_button_flags, action);
3838         current_button_flags = 0;
3839     }
3840     else
3841         pushBack();
3842     return 0;
3843 }
3844 static int c_on_key(map_t*args)
3845 {
3846     char*key = lu(args, "key");
3847     char*action = "";
3848     if(strlen(key)==1) {
3849         /* ascii */
3850         if(key[0]>=32) {
3851             current_button_flags |= 0x4000 + (key[0]*0x200);
3852         } else {
3853             syntaxerror("invalid character: %c"+key[0]);
3854             return 1;
3855         }
3856     } else {
3857         /* TODO:
3858            <ctrl-x> = 0x200*(x-'a')
3859            esc = = 0x3600
3860            space = = 0x4000;
3861         */
3862         syntaxerror("invalid key: %s",key);
3863     }
3864     readToken();
3865     if(type == RAWDATA) {
3866         action = text;
3867         s_buttonaction(current_button_flags, action);
3868         current_button_flags = 0;
3869     }
3870     else
3871         pushBack();
3872     return 0;
3873 }
3874
3875 static int c_edittext(map_t*args)
3876 {
3877  //"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"},
3878     char*name = lu(args, "name");
3879     char*font = lu(args, "font");
3880     int size = (int)(1024*parsePxOrPercent(font, lu(args, "size")));
3881     int width = parseTwip(lu(args, "width"));
3882     int height = parseTwip(lu(args, "height"));
3883     char*text  = lu(args, "text");
3884     RGBA color = parseColor(lu(args, "color"));
3885     int maxlength = parseInt(lu(args, "maxlength"));
3886     char*variable = lu(args, "variable");
3887     char*passwordstr = lu(args, "password");
3888     char*wordwrapstr = lu(args, "wordwrap");
3889     char*multilinestr = lu(args, "multiline");
3890     char*htmlstr = lu(args, "html");
3891     char*noselectstr = lu(args, "noselect");
3892     char*readonlystr = lu(args, "readonly");
3893     char*borderstr = lu(args, "border");
3894     char*autosizestr = lu(args, "autosize");
3895     char*alignstr = lu(args, "align");
3896     int align = -1;
3897
3898     int flags = 0;
3899     if(!strcmp(passwordstr, "password")) flags |= ET_PASSWORD;
3900     if(!strcmp(wordwrapstr, "wordwrap")) flags |= ET_WORDWRAP;
3901     if(!strcmp(multilinestr, "multiline")) flags |= ET_MULTILINE;
3902     if(!strcmp(readonlystr, "readonly")) flags |= ET_READONLY;
3903     if(!strcmp(htmlstr, "html")) flags |= ET_HTML;
3904     if(!strcmp(noselectstr, "noselect")) flags |= ET_NOSELECT;
3905     if(!strcmp(borderstr, "border")) flags |= ET_BORDER;
3906     if(!strcmp(autosizestr, "autosize")) flags |= ET_AUTOSIZE;
3907     if(!strcmp(alignstr, "left") || !*alignstr) align = ET_ALIGN_LEFT;
3908     else if(!strcmp(alignstr, "right")) align = ET_ALIGN_RIGHT;
3909     else if(!strcmp(alignstr, "center")) align = ET_ALIGN_CENTER;
3910     else if(!strcmp(alignstr, "justify")) align = ET_ALIGN_JUSTIFY;
3911     else syntaxerror("Unknown alignment: %s", alignstr);
3912
3913     s_edittext(name, font, size, width, height, text, &color, maxlength, variable, flags, align);
3914     return 0;
3915 }
3916
3917 static int c_morphshape(map_t*args) {return fakechar(args);}
3918 static int c_movie(map_t*args) {return fakechar(args);}
3919
3920 static char* readfile(const char*filename)
3921 {
3922     FILE*fi = fopen(filename, "rb");
3923     int l;
3924     char*text;
3925     if(!fi)
3926         syntaxerror("Couldn't find file %s: %s", filename, strerror(errno));
3927     fseek(fi, 0, SEEK_END);
3928     l = ftell(fi);
3929     fseek(fi, 0, SEEK_SET);
3930     text = rfx_alloc(l+1);
3931     fread(text, l, 1, fi);
3932     text[l]=0;
3933     fclose(fi);
3934     return text;
3935 }
3936
3937 static int c_action(map_t*args)
3938 {
3939     char* filename  = map_lookup(args, "filename");
3940     if(!filename ||!*filename) {
3941         readToken();
3942         if(type != RAWDATA) {
3943             syntaxerror("colon (:) expected");
3944         }
3945         s_action(text);
3946     } else {
3947         s_action(readfile(filename));
3948     }
3949
3950     return 0;
3951 }
3952
3953 static int c_initaction(map_t*args)
3954 {
3955     char* character = lu(args, "name");
3956     char* filename  = map_lookup(args, "filename");
3957     if(!filename ||!*filename) {
3958         readToken();
3959         if(type != RAWDATA) {
3960             syntaxerror("colon (:) expected");
3961         }
3962         s_initaction(character, text);
3963     } else {
3964         s_initaction(character, readfile(filename));
3965     }
3966
3967     return 0;
3968 }
3969
3970 static struct {
3971     char*command;
3972     command_func_t* func;
3973     char*arguments;
3974 } arguments[] =
3975 {{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no @export=1"},
3976  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
3977  // "import" type stuff
3978  {"swf", c_swf, "name filename"},
3979  {"shape", c_swf, "name filename"},
3980  {"jpeg", c_image, "name filename quality=80%"},
3981  {"png", c_image, "name filename"},
3982  {"movie", c_movie, "name filename"},
3983  {"sound", c_sound, "name filename"},
3984  {"font", c_font, "name filename glyphs="},
3985  {"soundtrack", c_soundtrack, "filename"},
3986  {"quicktime", c_quicktime, "url"},
3987
3988     // generators of primitives
3989
3990  {"define", c_define, "name value=0"},
3991  {"point", c_point, "name x=0 y=0"},
3992  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
3993  {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5 damping=2 slope=0"},
3994  {"outline", c_outline, "name format=simple"},
3995  {"textshape", c_textshape, "name font size=100% text"},
3996
3997     // filters
3998  {"blur", c_blur, "name blur= blurx= blury= passes=1"},
3999  {"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"},
4000  {"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"},
4001  {"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"},
4002
4003     // character generators
4004  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
4005  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
4006  {"filled", c_primitive, "name outline color=white line=1 @fill=none"},
4007
4008  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
4009  {"text", c_text, "name text font size=100% color=white"},
4010  {"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="},
4011  {"morphshape", c_morphshape, "name start end"},
4012  {"button", c_button, "name"},
4013     {"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="},
4014     {"on_press", c_on_press, "position=inside"},
4015     {"on_release", c_on_release, "position=anywhere"},
4016     {"on_move_in", c_on_move_in, "state=not_pressed"},
4017     {"on_move_out", c_on_move_out, "state=not_pressed"},
4018     {"on_key", c_on_key, "key=any"},
4019
4020     // control tags
4021  {"play", c_play, "name loop=0 @nomultiple=0"},
4022  {"stop", c_stop, "name= "},
4023  {"nextframe", c_nextframe, "name"},
4024  {"previousframe", c_previousframe, "name"},
4025
4026     // object placement tags
4027  {"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="},
4028  {"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="},
4029  {"move", c_move,       "name x= y= interpolation=linear"},
4030  {"smove", c_smove,     "name x= y= interpolation=linear"},
4031  {"sweep", c_sweep,     "name x= y= r= dir=counterclockwise arc=short interpolation=linear"},
4032  {"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"},
4033  //{"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
4034  {"schange", c_schange, "name red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
4035  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
4036  {"del", c_del, "name"},
4037     // virtual object placement
4038  {"texture", c_texture, "<i> x=0 y=0 width= height= scale= scalex= scaley= r= shear= rotate="},
4039     // switching
4040  {"toggle", c_toggle, "name fixed_alignment="},
4041
4042     // commands which start a block
4043 //startclip (see above)
4044  {"sprite", c_sprite, "name scalinggrid="},
4045  {"action", c_action, "filename="},
4046  {"initaction", c_initaction, "name filename="},
4047
4048  {"end", c_end, ""}
4049 };
4050
4051
4052 static map_t parseArguments(char*command, char*pattern)
4053 {
4054     char*x;
4055     char*d,*e;
4056
4057     string_t name[64];
4058     string_t value[64];
4059     int set[64];
4060     int isboolean[64];
4061     int pos;
4062     int len;
4063     int t;
4064     string_t t1,t2;
4065     map_t result;
4066     map_init(&result);
4067
4068     string_set(&t1, "commandname");
4069     string_set(&t2, command);
4070     map_put(&result, t1, t2);
4071
4072     if(!pattern || !*pattern)
4073         return result;
4074
4075     x = pattern;
4076
4077     pos = 0;
4078
4079     if(!strncmp("<i> ", x, 3)) {
4080         readToken();
4081         if(type == COMMAND || type == RAWDATA) {
4082             pushBack();
4083             syntaxerror("character name expected");
4084         }
4085         name[pos].str = "instance";
4086         name[pos].len = 8;
4087         value[pos].str = text;
4088         value[pos].len = strlen(text);
4089         set[pos] = 1;
4090         pos++;
4091
4092         if(type == ASSIGNMENT)
4093             readToken();
4094
4095         name[pos].str = "character";
4096         name[pos].len = 9;
4097         value[pos].str = text;
4098         value[pos].len = strlen(text);
4099         set[pos] = 1;
4100         pos++;
4101
4102         x+=4;
4103     }
4104
4105     while(*x) {
4106         isboolean[pos] = (x[0] =='@');
4107         if(isboolean[pos])
4108             x++;
4109
4110         d = strchr(x, ' ');
4111         e = strchr(x, '=');
4112         if(!d)
4113             d=&x[strlen(x)];
4114         set[pos] = 0;
4115
4116         if(!e || d<e) {
4117             // no default
4118             name[pos].str = x;
4119             name[pos].len = d-x;
4120             value[pos].str = 0;
4121             value[pos].len = 0;
4122         } else {
4123             name[pos].str = x;
4124             name[pos].len = e-x;
4125             value[pos].str = e+1;
4126             value[pos].len = d-e-1;
4127         }
4128         pos++;
4129         if(!*d) break;
4130         x=d+1;
4131     }
4132     len = pos;
4133
4134 /*    for(t=0;t<len;t++) {
4135         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
4136                 isboolean[t]?"(boolean)":"");
4137     }*/
4138
4139     while(1) {
4140         readToken();
4141         if(type == RAWDATA || type == COMMAND) {
4142             pushBack();
4143             break;
4144         }
4145
4146         // first, search for boolean arguments
4147         for(pos=0;pos<len;pos++)
4148         {
4149             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
4150                 set[pos] = 1;
4151                 if(type == ASSIGNMENT)
4152                     readToken();
4153                 value[pos].str = text;
4154                 value[pos].len = strlen(text);
4155                 /*printf("setting boolean parameter %s (to %s)\n",
4156                         strdup_n(name[pos], namelen[pos]),
4157                         strdup_n(value[pos], valuelen[pos]));*/
4158                 break;
4159             }
4160         }
4161
4162         // second, search for normal arguments
4163         if(pos==len)
4164         for(pos=0;pos<len;pos++)
4165         {
4166             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
4167                (type != ASSIGNMENT && !set[pos])) {
4168                 if(set[pos]) {
4169                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
4170                 }
4171                 if(type == ASSIGNMENT)
4172                     readToken();
4173                 set[pos] = 1;
4174                 value[pos].str = text;
4175                 value[pos].len = strlen(text);
4176 #if 0//def DEBUG
4177                 printf("setting parameter %s (to %s)\n",
4178                         strdup_n(name[pos].str, name[pos].len),
4179                         strdup_n(value[pos].str, value[pos].len));
4180 #endif
4181                 break;
4182             }
4183         }
4184         if(pos==len) {
4185             syntaxerror("Illegal argument \"%s\" to .%s", text, command);
4186         }
4187     }
4188 #if 0//def DEBUG
4189     for(t=0;t<len;t++) {
4190         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
4191     }
4192 #endif
4193     for(t=0;t<len;t++) {
4194         if(value[t].str && value[t].str[0] == '*') {
4195             //relative default- take value from some other parameter
4196             int s;
4197             for(s=0;s<len;s++) {
4198                 if(value[s].len == value[t].len-1 &&
4199                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
4200                     value[t].str = value[s].str;
4201             }
4202         }
4203         if(value[t].str == 0) {
4204             pushBack();
4205             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
4206         }
4207     }
4208
4209     /* ok, now construct the dictionary from the parameters */
4210
4211     for(t=0;t<len;t++)
4212     {
4213         map_put(&result, name[t], value[t]);
4214     }
4215     return result;
4216 }
4217 static void parseArgumentsForCommand(char*command)
4218 {
4219     int t;
4220     map_t args;
4221     int nr = -1;
4222     msg("<verbose> parse Command: %s (line %d)", command, line);
4223
4224     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
4225         if(!strcmp(arguments[t].command, command)) {
4226
4227             /* ugly hack- will be removed soon (once documentation and .sc generating
4228                utilities have been changed) */
4229             if(!strcmp(command, "swf") && !stackpos) {
4230                 warning("Please use .flash instead of .swf- this will be mandatory soon");
4231                 command = "flash";
4232                 t = 0;
4233             }
4234
4235             args = parseArguments(command, arguments[t].arguments);
4236             nr = t;
4237             break;
4238         }
4239     }
4240     if(nr<0)
4241         syntaxerror("command %s not known", command);
4242
4243     // catch missing .flash directives at the beginning of a file
4244     if(strcmp(command, "flash") && !stackpos)
4245     {
4246         syntaxerror("No movie defined- use .flash first");
4247     }
4248
4249 #ifdef DEBUG
4250     printf(".%s\n", command);fflush(stdout);
4251     map_dump(&args, stdout, "\t");fflush(stdout);
4252 #endif
4253
4254     (*arguments[nr].func)(&args);
4255
4256     /*if(!strcmp(command, "button") ||
4257        !strcmp(command, "action")) {
4258         while(1) {
4259             readToken();
4260             if(type == COMMAND) {
4261                 if(!strcmp(text, "end"))
4262                     break;
4263                 else {
4264                     pushBack();
4265                     break;
4266                 }
4267             }
4268         }
4269     }*/
4270
4271     map_clear(&args);
4272     return;
4273 }
4274
4275
4276 /* for now only intended to find what glyphs of each font are to be included in the swf file.
4277  * Therefore some knowledge about the command is assumed i.e. it is font/text-related
4278  * No syntax checking is done */
4279 static void analyseArgumentsForCommand(char*command)
4280 {
4281     int t;
4282     map_t args;
4283     char* fontfile;
4284     int nr = -1;
4285     msg("<verbose> analyse Command: %s (line %d)", command, line);
4286
4287     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++)
4288     {
4289         if(!strcmp(arguments[t].command, command))
4290         {
4291             args = parseArguments(command, arguments[t].arguments);
4292             nr = t;
4293             break;
4294         }
4295     }
4296 #ifdef DEBUG
4297     printf(".%s\n", command);fflush(stdout);
4298     map_dump(&args, stdout, "\t");fflush(stdout);
4299 #endif
4300     char* name = lu(&args, "name");
4301     if (!strcmp(command, "font"))
4302     {
4303         if(dictionary_lookup(&fonts, name))
4304             syntaxerror("font %s defined twice", name);
4305
4306         SWFFONT* font;
4307         fontfile = lu(&args, "filename");
4308         font = swf_LoadFont(fontfile);
4309         if(font == 0) {
4310             warning("Couldn't open font file \"%s\"", fontfile);
4311             font = (SWFFONT*)malloc(sizeof(SWFFONT));
4312             memset(font, 0, sizeof(SWFFONT));
4313         }
4314         swf_FontUseUTF8(font, lu(&args, "glyphs"));
4315         swf_FontPrepareForEditText(font);
4316         dictionary_put2(&fonts, name, font);
4317     }
4318     else
4319     {
4320         SWFFONT* font = dictionary_lookup(&fonts, lu(&args, "font"));
4321         if (!font)
4322             syntaxerror("font %s is not known in line %d", lu(&args, "font"), line);
4323         else
4324             if (!strcmp(command, "edittext"))
4325                 swf_FontUseAll(font);
4326             else
4327                 swf_FontUseUTF8(font, lu(&args, "text"));
4328     }
4329     map_clear(&args);
4330     return;
4331 }
4332
4333 void skipParameters()
4334 {
4335         do
4336                 readToken();
4337         while (type != COMMAND);
4338         pushBack();
4339 }
4340
4341 void findFontUsage()
4342 {
4343     char* fontRelated = "font;text;textshape;edittext;";
4344     while(!noMoreTokens())
4345     {
4346         readToken();
4347         if(type != COMMAND)
4348             syntaxerror("command expected");
4349         if (strstr(fontRelated, text))
4350             analyseArgumentsForCommand(text);
4351         else
4352             if(strcmp(text, "end"))
4353                 skipParameters();
4354     }
4355 }
4356
4357 void firstPass()
4358 {
4359     pos = 0;
4360     id = 0;
4361     dictionary_init(&fonts);
4362     cleanUp = &freeFontDictionary;
4363     findFontUsage();
4364 }
4365
4366 int main (int argc,char ** argv)
4367 {
4368     int t;
4369     processargs(argc, argv);
4370     initLog(0,-1,0,0,-1,verbose);
4371
4372     if(!filename) {
4373         args_callback_usage(argv[0]);
4374         exit(1);
4375     }
4376
4377     file = generateTokens(filename);
4378     if(!file) {
4379         fprintf(stderr, "parser returned error.\n");
4380         return 1;
4381     }
4382     firstPass();
4383     pos=0;
4384     t=0;
4385
4386     while(!noMoreTokens()) {
4387         readToken();
4388         if(type != COMMAND)
4389             syntaxerror("command expected");
4390         parseArgumentsForCommand(text);
4391     }
4392
4393     s_close();
4394     freeTokens(file);
4395
4396     return 0;
4397 }
4398