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