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