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