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