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