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