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