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