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