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