added (long) GPL headers.
[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/log.h"
32 #include "../lib/args.h"
33 #include "../lib/q.h"
34 #include "parser.h"
35 #include "wav.h"
36
37 //#define DEBUG
38
39 static char * filename = 0;
40 static char * outputname = "output.swf";
41 static int verbose = 2;
42 static int override_outputname = 0;
43
44 static struct options_t options[] =
45 {
46  {"o","output"},
47  {"v","verbose"},
48  {"V","version"},
49  {0,0}
50 };
51     
52 int args_callback_option(char*name,char*val)
53 {
54     if(!strcmp(name, "V")) {
55         printf("swfc - part of %s %s\n", PACKAGE, VERSION);
56         exit(0);
57     }
58     else if(!strcmp(name, "o")) {
59         outputname = val;
60         override_outputname = 1;
61         return 1;
62     }
63     else if(!strcmp(name, "v")) {
64         verbose ++;
65         return 0;
66     }
67     else {
68         printf("Unknown option: -%s\n", name);
69         exit(1);
70     }
71     return 0;
72 }
73 int args_callback_longoption(char*name,char*val)
74 {
75     return args_long2shortoption(options, name, val);
76 }
77 void args_callback_usage(char*name)
78 {
79     printf("Usage: %s [-o filename] file.wav\n", name);
80     printf("\t-v , --verbose\t\t\t Be more verbose\n");
81     printf("\t-o , --output filename\t\t set output filename (default: output.swf)\n");
82     printf("\t-V , --version\t\t\t Print program version and exit\n");
83 }
84 int args_callback_command(char*name,char*val)
85 {
86     if(filename) {
87         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
88                  filename, name);
89     }
90     filename = name;
91     return 0;
92 }
93
94 static struct token_t* file;
95
96 static int pos;
97 static char*text;
98 static int textlen;
99 static int type;
100 static int line;
101 static int column;
102
103 static void syntaxerror(char*format, ...)
104 {
105     char buf[1024];
106     va_list arglist;
107     va_start(arglist, format);
108     vsprintf(buf, format, arglist);
109     va_end(arglist);
110     printf("\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
111     exit(1);
112 }
113
114 static void warning(char*format, ...)
115 {
116     char buf[1024];
117     va_list arglist;
118     va_start(arglist, format);
119     vsprintf(buf, format, arglist);
120     va_end(arglist);
121     printf("\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
122 }
123    
124 static void readToken()
125 {
126     type = file[pos].type;
127     if(type == END) {
128         syntaxerror("unexpected end of file");
129     }
130     text = file[pos].text;
131     textlen = strlen(text);
132     line = file[pos].line;
133     column = file[pos].column;
134     pos++;
135     //printf("---> %d(%s) %s\n", type, type_names[type], text);
136 }
137
138 static void pushBack()
139 {
140     int p;
141     if(!pos) syntaxerror("internal error 3");
142     pos--;
143     p = pos;
144     if(p) p--;
145     text = file[p].text;
146     textlen = strlen(text);
147     type = file[p].type;
148     line = file[p].line;
149     column = file[p].column;
150 }
151
152 static int noMoreTokens()
153 {
154     if(file[pos].type == END)
155         return 1;
156     return 0;
157 }
158
159 // ------------------------------ swf routines ----------------------------
160 struct _character;
161 static struct level
162 {
163    int type; //0=swf, 1=sprite, 2=clip
164
165    /* for swf (0): */
166    SWF*swf;
167    char*filename; 
168
169    /* for sprites (1): */
170    TAG*tag;
171    U16 id;
172    char*name;
173    U16 olddepth;
174    int oldframe;
175    dictionary_t oldinstances;
176    SRECT oldrect;
177
178 } stack[256];
179 static int stackpos = 0;
180
181 static dictionary_t characters;
182 static char idmap[65536];
183 static TAG*tag = 0; //current tag
184
185 static int id; //current character id
186 static int currentframe; //current frame in current level
187 static SRECT currentrect; //current bounding box in current level
188 static U16 currentdepth;
189 static dictionary_t instances;
190 static dictionary_t fonts;
191 static dictionary_t sounds;
192
193 typedef struct _parameters {
194     int x,y; 
195     float scalex, scaley; 
196     CXFORM cxform;
197     float rotate;
198     float shear;
199     SPOINT pivot;
200     SPOINT pin;
201 } parameters_t;
202
203 typedef struct _character {
204     TAG*definingTag;
205     U16 id;
206     SRECT size;
207 } character_t;
208
209 typedef struct _instance {
210     character_t*character;
211     U16 depth;
212     parameters_t parameters;
213     TAG* lastTag; //last tag which set the object
214     U16 lastFrame; //frame lastTag is in
215 } instance_t;
216
217 static void character_init(character_t*c)
218 {
219     memset(c, 0, sizeof(character_t));
220 }
221 static character_t* character_new()
222 {
223     character_t*c;
224     c = (character_t*)malloc(sizeof(character_t));
225     character_init(c);
226     return c;
227 }
228 static void instance_init(instance_t*i)
229 {
230     memset(i, 0, sizeof(instance_t));
231 }
232 static instance_t* instance_new()
233 {
234     instance_t*c;
235     c = (instance_t*)malloc(sizeof(instance_t));
236     instance_init(c);
237     return c;
238 }
239
240 static void incrementid()
241 {
242     while(idmap[++id]) {
243         if(id==65535)
244             syntaxerror("Out of character ids.");
245     }
246     idmap[id] = 1;
247 }
248
249 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
250 {
251     character_t* c = character_new();
252     
253     c->definingTag = ctag;
254     c->id = id;
255     c->size = r;
256     if(dictionary_lookup(&characters, name))
257         syntaxerror("character %s defined twice", name);
258     dictionary_put2(&characters, name, c);
259
260     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
261     swf_SetU16(tag, id);
262     swf_SetString(tag, name);
263     tag = swf_InsertTag(tag, ST_EXPORTASSETS);
264     swf_SetU16(tag, 1);
265     swf_SetU16(tag, id);
266     swf_SetString(tag, name);
267 }
268 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
269 {
270     instance_t* i = instance_new();
271     i->character = c;
272     i->depth = depth;
273     //swf_GetMatrix(0, &i->matrix);
274     if(dictionary_lookup(&instances, name))
275         syntaxerror("object %s defined twice", name);
276     dictionary_put2(&instances, name, i);
277     return i;
278 }
279
280 static void parameters_set(parameters_t*p, int x,int y, float scalex, float scaley, float rotate, float shear, SPOINT pivot, SPOINT pin, CXFORM cxform)
281 {
282     p->x = x; p->y = y; 
283     p->scalex = scalex; p->scaley = scaley;
284     p->pin    = pin; p->pivot = pivot;
285     p->rotate = rotate; p->cxform = cxform;
286     p->shear = shear;
287 }
288
289 static void parameters_clear(parameters_t*p)
290 {
291     p->x = 0; p->y = 0; 
292     p->scalex = 1.0; p->scaley = 1.0;
293     p->pin.x = 1; p->pin.y = 0;
294     p->pivot.x = 0; p->pivot.y = 0;
295     p->rotate = 0; 
296     p->shear = 0; 
297     swf_GetCXForm(0, &p->cxform, 1);
298 }
299
300 static void makeMatrix(MATRIX*m, parameters_t*p)
301 {
302     SPOINT h;
303     float sx,r1,r0,sy;
304
305     /*        /sx r1\ /x\ 
306      *        \r0 sy/ \y/
307      */
308
309     sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979);
310     r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
311     r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979);
312     sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
313
314     m->sx = (int)(sx*65536+0.5);
315     m->r1 = (int)(r1*65536+0.5);
316     m->r0 = (int)(r0*65536+0.5);
317     m->sy = (int)(sy*65536+0.5);
318
319     m->tx = m->ty = 0;
320
321     h = swf_TurnPoint(p->pin, m);
322     m->tx = p->x - h.x;
323     m->ty = p->y - h.y;
324 }
325
326 static MATRIX s_instancepos(instance_t*i, parameters_t*p)
327 {
328     MATRIX m;
329     SRECT r;
330     makeMatrix(&m, p);
331     r = swf_TurnRect(i->character->size, &m);
332     if(currentrect.xmin == 0 && currentrect.ymin == 0 && 
333        currentrect.xmax == 0 && currentrect.ymax == 0)
334         currentrect = r;
335     else
336         swf_ExpandRect2(&currentrect, &r);
337     return m;
338 }
339
340 void s_swf(char*name, SRECT r, int version, int fps, int compress)
341 {
342     SWF*swf = (SWF*)malloc(sizeof(SWF));
343     RGBA rgb;
344
345     if(stackpos)
346         syntaxerror(".swf blocks can't be nested");
347
348     memset(swf, 0, sizeof(swf));
349     swf->fileVersion = version;
350     swf->movieSize = r;
351     swf->frameRate = fps;
352     swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
353     swf->compressed = compress;
354     rgb.r = 0x00;rgb.g = 0x00;rgb.b = 0x00;
355     swf_SetRGB(tag,&rgb);
356     
357     if(stackpos==sizeof(stack)/sizeof(stack[0]))
358         syntaxerror("too many levels of recursion");
359     
360     dictionary_init(&characters);
361     dictionary_init(&instances);
362     dictionary_init(&fonts);
363     dictionary_init(&sounds);
364
365     memset(&stack[stackpos], 0, sizeof(stack[0]));
366     stack[stackpos].type = 0;
367     stack[stackpos].filename = strdup(name);
368     stack[stackpos].swf = swf;
369     stack[stackpos].oldframe = -1;
370     stackpos++;
371     id = 0;
372
373     currentframe = 0;
374     memset(&currentrect, 0, sizeof(currentrect));
375     currentdepth = 1;
376     
377     memset(idmap, 0, sizeof(idmap));
378     incrementid();
379 }
380
381 void s_sprite(char*name)
382 {
383     tag = swf_InsertTag(tag, ST_DEFINESPRITE);
384     swf_SetU16(tag, id); //id
385     swf_SetU16(tag, 0); //frames
386
387     memset(&stack[stackpos], 0, sizeof(stack[0]));
388     stack[stackpos].type = 1;
389     stack[stackpos].oldframe = currentframe;
390     stack[stackpos].olddepth = currentdepth;
391     stack[stackpos].oldrect = currentrect;
392     stack[stackpos].oldinstances = instances;
393     stack[stackpos].tag = tag;
394     stack[stackpos].id = id;
395     stack[stackpos].name = strdup(name);
396    
397     /* FIXME: those four fields should be bundled together */
398     dictionary_init(&instances);
399     currentframe = 0;
400     currentdepth = 1;
401     memset(&currentrect, 0, sizeof(currentrect));
402
403     stackpos++;
404     incrementid();
405 }
406
407 static void s_endSprite()
408 {
409     SRECT r = currentrect;
410     stackpos--;
411    
412     /* TODO: before clearing, prepend "<spritename>." to names and
413              copy into old instances dict */
414     dictionary_clear(&instances);
415
416     currentframe = stack[stackpos].oldframe;
417     currentrect = stack[stackpos].oldrect;
418     currentdepth = stack[stackpos].olddepth;
419     instances = stack[stackpos].oldinstances;
420     tag = swf_InsertTag(tag, ST_END);
421
422     tag = stack[stackpos].tag;
423     swf_FoldSprite(tag);
424     if(tag->next != 0)
425         syntaxerror("internal error(7)");
426
427     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
428     free(stack[stackpos].name);
429 }
430
431 static void s_endSWF()
432 {
433     int fi;
434     SWF* swf;
435     char*filename;
436     stackpos--;
437
438     swf = stack[stackpos].swf;
439     filename = stack[stackpos].filename;
440     
441     tag = swf_InsertTag(tag, ST_SHOWFRAME);
442     tag = swf_InsertTag(tag, ST_END);
443
444     swf_OptimizeTagOrder(swf);
445
446     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin))
447         swf->movieSize = currentrect; /* "autocrop" */
448     
449     if(!(swf->movieSize.xmax-swf->movieSize.xmin) || !(swf->movieSize.ymax-swf->movieSize.ymin)) {
450         swf->movieSize.xmax += 20; /* 1 by 1 pixels */
451         swf->movieSize.ymax += 20;
452     }
453
454     fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
455     if(fi<0) {
456         syntaxerror("couldn't create output file %s", filename);
457     }
458     if(swf->compressed) 
459         {if(swf_WriteSWC(fi, swf)<0) syntaxerror("WriteSWC() failed.\n");}
460     else
461         {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
462
463     close(fi);
464     
465     dictionary_clear(&instances);
466     dictionary_clear(&characters);
467     dictionary_clear(&fonts);
468     dictionary_clear(&sounds);
469
470     swf_FreeTags(swf);
471     free(swf);
472     free(filename);
473 }
474 void s_close()
475 {
476     if(stackpos) {
477         if(stack[stackpos-1].type == 0)
478             syntaxerror("End of file encountered in .swf block");
479         if(stack[stackpos-1].type == 1)
480             syntaxerror("End of file encountered in .sprite block");
481         if(stack[stackpos-1].type == 2)
482             syntaxerror("End of file encountered in .clip block");
483     }
484 }
485
486 int s_getframe()
487 {
488     return currentframe;
489 }
490
491 void s_frame(int nr)
492 {
493     int t;
494     for(t=currentframe;t<nr;t++) {
495         tag = swf_InsertTag(tag, ST_SHOWFRAME);
496     }
497     currentframe = nr;
498 }
499         
500 RGBA black={r:0,g:0,b:0,a:0};
501 void s_box(char*name, int width, int height, RGBA color, int linewidth, RGBA fill, int dofill)
502 {
503     SRECT r;
504     SHAPE* s;
505     int ls1,fs1=0;
506     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
507     swf_ShapeNew(&s);
508     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
509     if(dofill)
510         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
511     swf_SetU16(tag,id);
512     r.xmin = -linewidth-linewidth/2;
513     r.ymin = -linewidth-linewidth/2;
514     r.xmax = width+linewidth+linewidth/2;
515     r.ymax = height+linewidth+linewidth/2;
516     swf_SetRect(tag,&r);
517     swf_SetShapeHeader(tag,s);
518     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
519     swf_ShapeSetLine(tag,s,width,0);
520     swf_ShapeSetLine(tag,s,0,height);
521     swf_ShapeSetLine(tag,s,-width,0);
522     swf_ShapeSetLine(tag,s,0,-height);
523     swf_ShapeSetEnd(tag);
524     swf_ShapeFree(s);
525    
526     s_addcharacter(name, id, tag, r);
527     incrementid();
528 }
529
530 void s_circle(char*name, int r, RGBA color, int linewidth, RGBA fill, int dofill)
531 {
532     SRECT rect;
533     SHAPE* s;
534     int ls1,fs1=0;
535     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
536     swf_ShapeNew(&s);
537     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
538     if(dofill)
539         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
540     swf_SetU16(tag,id);
541     rect.xmin = -linewidth-linewidth/2;
542     rect.ymin = -linewidth-linewidth/2;
543     rect.xmax = 2*r+linewidth+linewidth/2;
544     rect.ymax = 2*r+linewidth+linewidth/2;
545
546     swf_SetRect(tag,&rect);
547     swf_SetShapeHeader(tag,s);
548     swf_ShapeSetAll(tag,s,0,0,ls1,fs1,0);
549     swf_ShapeSetCircle(tag, s, r,r,r,r);
550     swf_ShapeSetEnd(tag);
551     swf_ShapeFree(s);
552    
553     s_addcharacter(name, id, tag, rect);
554     incrementid();
555 }
556
557 void s_textshape(char*name, char*fontname, char*_text, RGBA color, int linewidth, RGBA fill, int dofill)
558 {
559     SRECT rect;
560     SHAPE* s;
561     int ls1,fs1=0;
562     int g;
563     U8*text = (U8*)_text;
564
565     SWFFONT*font;
566     font = dictionary_lookup(&fonts, fontname);
567     if(!font)
568         syntaxerror("font \"%s\" not known!", fontname);
569     if(!dofill)
570         syntaxerror("textshapes must be filled", fontname);
571
572     if(text[0] >= font->maxascii || font->ascii2glyph[text[0]]<0) {
573         warning("no character 0x%02x (%c) in font \"%s\"", text[0], text[0], fontname);
574         s_box(name, 0, 0, black, 20, black, 0);
575         return;
576     }
577     g = font->ascii2glyph[text[0]];
578     rect = font->layout->bounds[g];
579
580     swf_ShapeNew(&s);
581     ls1 = swf_ShapeAddLineStyle(s,linewidth,&color);
582     if(dofill)
583         fs1 = swf_ShapeAddSolidFillStyle(s,&fill);
584      
585     tag = swf_InsertTag(tag, ST_DEFINESHAPE);
586     swf_SetU16(tag,id);
587     swf_SetRect(tag, &rect);
588     swf_SetShapeStyles(tag, s);
589     swf_SetSimpleShape(tag, font->glyph[g].shape);
590     swf_ShapeFree(s);
591     
592     s_addcharacter(name, id, tag, rect);
593     incrementid();
594 }
595 void s_text(char*name, char*fontname, char*text, int size, RGBA color)
596 {
597     SRECT r;
598
599     SWFFONT*font;
600     font = dictionary_lookup(&fonts, fontname);
601     if(!font)
602         syntaxerror("font \"%s\" not known!", fontname);
603     
604     tag = swf_InsertTag(tag, ST_DEFINETEXT2);
605     swf_SetU16(tag, id);
606     if(!font->numchars) {
607         s_box(name, 0, 0, black, 20, black, 0);
608         return;
609     }
610     r = swf_SetDefineText(tag, font, &color, text, size);
611    
612     s_addcharacter(name, id, tag, r);
613     incrementid();
614 }
615
616 void dumpSWF(SWF*swf)
617 {
618     TAG* tag = swf->firstTag;
619     printf("vvvvvvvvvvvvvvvvvvvvv\n");
620     while(tag) {
621         printf("%8d %s\n", tag->len, swf_TagGetName(tag));
622         tag = tag->next;
623     }
624     printf("^^^^^^^^^^^^^^^^^^^^^\n");
625 }
626     
627 void s_font(char*name, char*filename)
628 {
629     int f;
630     SWF swf;
631     SWFFONT* font;
632     f = open(filename,O_RDONLY|O_BINARY);
633     if (f<0) { 
634         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
635         font = (SWFFONT*)malloc(sizeof(SWFFONT));
636         memset(font, 0, sizeof(SWFFONT));
637         dictionary_put2(&fonts, name, font);
638         return;
639     }
640     font = 0;
641     if (swf_ReadSWF(f,&swf)>=0) { 
642         swf_FontExtract(&swf, 0x4e46, &font);
643         swf_FreeTags(&swf);
644     }
645     close(f);
646     if (font==0) { 
647         syntaxerror("File \"%s\" isn't a valid rfxswf font file", filename);
648     }
649
650     if(0)
651     {
652         /* fix the layout. Only needed for old fonts */
653         int t;
654         for(t=0;t<font->numchars;t++) {
655             font->glyph[t].advance = 0;
656         }
657         font->layout = 0;
658         swf_FontCreateLayout(font);
659     }
660
661     font->id = id;
662     tag = swf_InsertTag(tag, ST_DEFINEFONT2);
663     swf_FontSetDefine2(tag, font);
664     incrementid();
665
666     if(dictionary_lookup(&fonts, name))
667         syntaxerror("font %s defined twice", name);
668     dictionary_put2(&fonts, name, font);
669 }
670
671 typedef struct _sound_t
672 {
673     U16 id;
674     TAG*tag;
675 } sound_t;
676
677 void s_sound(char*name, char*filename)
678 {
679     struct WAV wav, wav2;
680     sound_t* sound;
681     U16*samples;
682     int numsamples;
683
684     if(!readWAV(filename, &wav)) {
685         warning("Couldn't read wav file \"%s\"", filename);
686         samples = 0;
687         numsamples = 0;
688     } else {
689         convertWAV2mono(&wav, &wav2, 44100);
690         samples = (U16*)wav2.data;
691         numsamples = wav2.size/2;
692         free(wav.data);
693     }
694
695     tag = swf_InsertTag(tag, ST_DEFINESOUND);
696     swf_SetU16(tag, id); //id
697     swf_SetSoundDefine(tag, samples, numsamples);
698    
699     sound = (sound_t*)malloc(sizeof(sound_t)); /* mem leak */
700     sound->tag = tag;
701     sound->id = id;
702
703     if(dictionary_lookup(&sounds, name))
704         syntaxerror("sound %s defined twice", name);
705     dictionary_put2(&sounds, name, sound);
706     
707     incrementid();
708
709     if(samples)
710         free(samples);
711 }
712
713 void s_playsound(char*name, int loops, int nomultiple, int stop)
714 {
715     sound_t* sound = dictionary_lookup(&sounds, name);
716     SOUNDINFO info;
717     if(!sound)
718         syntaxerror("Don't know anything about sound \"%s\"", name);
719
720     tag = swf_InsertTag(tag, ST_STARTSOUND);
721     swf_SetU16(tag, sound->id); //id
722     memset(&info, 0, sizeof(info));
723     info.stop = stop;
724     info.loops = loops;
725     info.nomultiple = nomultiple;
726     swf_SetSoundInfo(tag, &info);
727 }
728
729 void s_shape(char*name, char*filename)
730 {
731     int f;
732     SWF swf;
733     TAG* ftag;
734     SRECT r;
735     TAG* s;
736     int level = 0;
737     U16 cutout[] = {ST_SETBACKGROUNDCOLOR, ST_PROTECT, ST_FREEALL, ST_REFLEX};
738     f = open(filename,O_RDONLY);
739     if (f<0) { 
740         warning("Couldn't open file \"%s\": %s", filename, strerror(errno));
741         s_box(name, 0, 0, black, 20, black, 0);
742         return;
743     }
744     if (swf_ReadSWF(f,&swf)<0) { 
745         warning("Only SWF files supported in .shape for now. File \"%s\" wasn't SWF.", filename);
746         s_box(name, 0, 0, black, 20, black, 0);
747         return;
748     }
749     close(f);
750
751     /* FIXME: The following sets the bounding Box for the character. 
752               It is wrong for two reasons:
753               a) It may be too small (in case objects in the movie clip at the borders)
754               b) it may be too big (because the poor movie never got autocropped)
755     */
756     r = swf.movieSize;
757     
758     s = tag = swf_InsertTag(tag, ST_DEFINESPRITE);
759     swf_SetU16(tag, id);
760     swf_SetU16(tag, 0);
761
762     swf_Relocate(&swf, idmap);
763
764     ftag = swf.firstTag;
765     level = 1;
766     while(ftag) {
767         int t;
768         for(t=0;t<sizeof(cutout)/sizeof(cutout[0]);t++)
769             if(cutout[t] == ftag->id) {
770                 ftag = ftag->next;
771                 continue;
772             }
773         if(ftag->id == ST_DEFINESPRITE && !swf_IsFolded(ftag))
774             level++;
775         if(ftag->id == ST_END)
776             level--;
777         if(!level)
778             break;
779         /* We simply dump all tags right after the sprite
780            header, relying on the fact that swf_OptimizeTagOrder() will
781            sort things out for us later. 
782            We also rely on the fact that the imported SWF is well-formed.
783          */
784         tag = swf_InsertTag(tag, ftag->id);
785         swf_SetBlock(tag, ftag->data, ftag->len);
786         ftag = ftag->next;
787     }
788     if(!ftag)
789         syntaxerror("Included file %s contains errors", filename);
790     tag = swf_InsertTag(tag, ST_END);
791
792     swf_FreeTags(&swf);
793
794     s_addcharacter(name, id, tag, r);
795     incrementid();
796 }
797 SRECT s_getCharBBox(char*name)
798 {
799     character_t* c = dictionary_lookup(&characters, name);
800     if(!c) syntaxerror("character '%s' unknown(2)", name);
801     return c->size;
802 }
803 SRECT s_getInstanceBBox(char*name)
804 {
805     instance_t * i = dictionary_lookup(&instances, name);
806     character_t * c;
807     if(!i) syntaxerror("instance '%s' unknown(4)", name);
808     c = i->character;
809     if(!c) syntaxerror("internal error(5)");
810     return c->size;
811 }
812 parameters_t s_getParameters(char*name)
813 {
814     instance_t * i = dictionary_lookup(&instances, name);
815     if(!i) syntaxerror("instance '%s' unknown(10)", name);
816     return i->parameters;
817 }
818 void s_startclip(char*instance, char*character, parameters_t p)
819 {
820     character_t* c = dictionary_lookup(&characters, character);
821     instance_t* i;
822     MATRIX m;
823     if(!c) {
824         syntaxerror("character %s not known", character);
825     }
826     i = s_addinstance(instance, c, currentdepth);
827     i->parameters = p;
828     m = s_instancepos(i, &p);
829     
830     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
831     /* TODO: should be ObjectPlaceClip, with clipdepth backpatched */
832     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
833     i->lastTag = tag;
834     i->lastFrame= currentframe;
835
836     stack[stackpos].tag = tag;
837     stack[stackpos].type = 2;
838     stackpos++;
839
840     currentdepth++;
841 }
842 void s_endClip()
843 {
844     SWFPLACEOBJECT p;
845     stackpos--;
846     swf_SetTagPos(stack[stackpos].tag, 0);
847     swf_GetPlaceObject(stack[stackpos].tag, &p);
848     p.clipdepth = currentdepth;
849     swf_ClearTag(stack[stackpos].tag);
850     swf_SetPlaceObject(stack[stackpos].tag, &p);
851     currentdepth++;
852 }
853
854 void s_put(char*instance, char*character, parameters_t p)
855 {
856     character_t* c = dictionary_lookup(&characters, character);
857     instance_t* i;
858     MATRIX m;
859     if(!c) {
860         syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
861     }
862     
863     i = s_addinstance(instance, c, currentdepth);
864     i->parameters = p;
865     m = s_instancepos(i, &p);
866     
867     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
868     swf_ObjectPlace(tag, c->id, currentdepth, &m, &p.cxform, instance);
869     i->lastTag = tag;
870     i->lastFrame = currentframe;
871     currentdepth++;
872 }
873
874 void s_jump(char*instance, parameters_t p)
875 {
876     instance_t* i = dictionary_lookup(&instances, instance);
877     MATRIX m;
878     if(!i) {
879         syntaxerror("instance %s not known", instance);
880     }
881
882     i->parameters = p;
883     m = s_instancepos(i, &p);
884
885     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
886     swf_ObjectMove(tag, i->depth, &m, &p.cxform);
887     i->lastTag = tag;
888     i->lastFrame = currentframe;
889 }
890
891 parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
892 {
893     parameters_t p;
894     float ratio;
895     if(num==0 || num==1)
896         return *p1;
897     ratio = (float)pos/(float)num;
898     
899     p.x = (p2->x-p1->x)*ratio + p1->x;
900     p.y = (p2->y-p1->y)*ratio + p1->y;
901     p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
902     p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
903     p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
904     p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
905
906     p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
907     p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
908     p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
909     p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
910
911     p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
912     p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
913     p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
914     p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
915
916     p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
917     p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
918     p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
919     p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
920     return p;
921 }
922
923 void s_change(char*instance, parameters_t p2)
924 {
925     instance_t* i = dictionary_lookup(&instances, instance);
926     MATRIX m;
927     parameters_t p1;
928     TAG*t;
929     int frame, allframes;
930     if(!i) {
931         syntaxerror("instance %s not known", instance);
932     }
933     p1 = i->parameters;
934     
935     allframes = currentframe - i->lastFrame - 1;
936     if(allframes < 0) {
937         warning(".change ignored. can only .put/.change an object once per frame.");
938         return;
939     }
940     
941     m = s_instancepos(i, &p2);
942     tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
943     swf_ObjectMove(tag, i->depth, &m, &p2.cxform);
944     i->parameters = p2;
945
946     /* o.k., we got the start and end point set. Now iterate though all the
947        tags in between, inserting object changes after each new frame */
948     t = i->lastTag;
949     i->lastTag = tag;
950     if(!t) syntaxerror("internal error(6)");
951     frame = 0;
952     while(frame < allframes) {
953         if(t->id == ST_SHOWFRAME) {
954             parameters_t p;
955             MATRIX m;
956             TAG*lt;
957             frame ++;
958             p = s_interpolate(&p1, &p2, frame, allframes);
959             m = s_instancepos(i, &p); //needed?
960             lt = swf_InsertTag(t, ST_PLACEOBJECT2);
961             i->lastFrame = currentframe;
962             swf_ObjectMove(lt, i->depth, &m, &p.cxform);
963             t = lt;
964             if(frame == allframes)
965                 break;
966         }
967         t = t->next;
968         if(!t) 
969             syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
970     }
971 }
972
973 void s_delinstance(char*instance)
974 {
975     instance_t* i = dictionary_lookup(&instances, instance);
976     if(!i) {
977         syntaxerror("instance %s not known", instance);
978     }
979     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
980     swf_SetU16(tag, i->depth);
981     dictionary_del(&instances, instance);
982 }
983
984 void s_qchange(char*instance, parameters_t p)
985 {
986 }
987
988 void s_end()
989 {
990     if(!stackpos)
991         syntaxerror(".end unexpected");
992     if(stack[stackpos-1].type == 0)
993         s_endSWF();
994     else if(stack[stackpos-1].type == 1)
995         s_endSprite();
996     else if(stack[stackpos-1].type == 2)
997         s_endClip();
998     else syntaxerror("internal error 1");
999 }
1000
1001 // ------------------------------------------------------------------------
1002
1003 typedef int command_func_t(map_t*args);
1004
1005 SRECT parseBox(char*str)
1006 {
1007     SRECT r;
1008     float xmin, xmax, ymin, ymax;
1009     char*x = strchr(str, 'x');
1010     char*d1=0,*d2=0;
1011     if(!strcmp(str, "autocrop")) {
1012         r.xmin = r.ymin = r.xmax = r.ymax = 0;
1013         return r;
1014     }
1015     if(!x) goto error;
1016     d1 = strchr(x+1, ':');
1017     if(d1)
1018         d2 = strchr(d1+1, ':');
1019     if(!d1) {
1020         if(sscanf(str, "%fx%f", &xmax, &ymax) < 2)
1021             goto error;
1022         xmin = ymin = 0;
1023     }
1024     else if(d1 && !d2) {
1025         if(sscanf(str, "%fx%f:%f", &xmax, &ymax, &xmin) < 3)
1026             goto error;
1027         xmax += xmin;
1028         ymin = 0;
1029     }
1030     else {
1031         if(sscanf(str, "%fx%f:%f:%f", &xmax, &ymax, &xmin, &ymin) < 4)
1032             goto error;
1033         xmax += xmin;
1034         ymax += ymin;
1035     }
1036     r.xmin = (SCOORD)(xmin*20);
1037     r.ymin = (SCOORD)(ymin*20);
1038     r.xmax = (SCOORD)(xmax*20);
1039     r.ymax = (SCOORD)(ymax*20);
1040     return r;
1041 error:
1042     syntaxerror("expression %s is not a valid bound Box.\nE.g. 1024x768 or 1024x768:30:30 would have been valid bounding Boxes.", str);
1043     return r;
1044 }
1045 float parseFloat(char*str)
1046 {
1047     return atof(str);
1048 }
1049 int parseInt(char*str)
1050 {
1051     int t;
1052     int l=strlen(str);
1053     int s=0;
1054     if(str[0]=='+' || str[0]=='-')
1055         s++;
1056
1057     for(t=s;t<l;t++)
1058         if(str[t]<'0' || str[t]>'9')
1059             syntaxerror("Not an Integer: \"%s\"", str);
1060     return atoi(str);
1061 }
1062 int parseTwip(char*str)
1063 {
1064     char*dot;
1065     int sign=1;
1066     if(str[0]=='+' || str[0]=='-') {
1067         if(str[0]=='-')
1068             sign = -1;
1069         str++;
1070     }
1071     dot = strchr(str, '.');
1072     if(!dot) {
1073         int l=strlen(str);
1074         int t;
1075         return sign*parseInt(str)*20;
1076     } else {
1077         int l=strlen(++dot);
1078         char*s;
1079         for(s=str;s<dot-1;s++)
1080             if(*s<'0' || *s>'9')
1081                 syntaxerror("Not a coordinate: \"%s\"", str);
1082         for(s=dot;*s;s++) {
1083             if(*s<'0' || *s>'9')
1084                 syntaxerror("Not a coordinate: \"%s\"", str);
1085         }
1086         if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1087             warning("precision loss: %s converted to twip", str);
1088             dot[2] = 0;
1089             l=2;
1090         }
1091         if(l==0)
1092             return sign*atoi(str)*20;
1093         if(l==1)
1094             return sign*atoi(str)*20+atoi(dot)*2;
1095         if(l==2)
1096             return sign*atoi(str)*20+atoi(dot)/5;
1097     }
1098     return 0;
1099 }
1100
1101 int isPoint(char*str)
1102 {
1103     if(strchr(str, '('))
1104         return 1;
1105     else
1106         return 0;
1107 }
1108
1109 SPOINT parsePoint(char*str)
1110 {
1111     SPOINT p;
1112     char tmp[80];
1113     int l = strlen(str);
1114     char*comma = strchr(str, ',');
1115     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1116         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1117     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1118     p.x = parseTwip(tmp);
1119     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1120     p.y = parseTwip(tmp);
1121     return p;
1122 }
1123
1124 int parseColor2(char*str, RGBA*color)
1125 {
1126     int l = strlen(str);
1127     int r,g,b,a;
1128     int t;
1129
1130     struct {unsigned char r,g,b;char*name;} colors[] =
1131     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1132     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1133     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1134     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1135     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1136     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1137     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1138     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1139     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1140     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1141     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1142     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1143
1144     a=255;r=g=b=0;
1145
1146     if(str[0]=='#' && (l==7 || l==9)) {
1147         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1148             return 0;
1149         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1150             return 0;
1151         color->r = r; color->g = g; color->b = b; color->a = a;
1152         return 1;
1153     }
1154     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1155         if(!strcmp(str, colors[t].name)) {
1156             r = colors[t].r;
1157             g = colors[t].g;
1158             b = colors[t].b;
1159             a = 255;
1160             color->r = r; color->g = g; color->b = b; color->a = a;
1161             return 1;
1162         }
1163     return 0;
1164
1165 }
1166 RGBA parseColor(char*str)
1167 {
1168     RGBA c;
1169     if(!parseColor2(str, &c))
1170         syntaxerror("Expression '%s' is not a color", str);
1171     return c;
1172 }
1173
1174 typedef struct _muladd {
1175     S16 mul;
1176     S16 add;
1177 } MULADD;
1178
1179 MULADD parseMulAdd(char*str)
1180 {
1181     float add, mul;
1182     char* str2 = (char*)malloc(strlen(str)+5);
1183     int i;
1184     MULADD m;
1185     strcpy(str2, str);
1186     strcat(str2, " 0");
1187     add = 0;
1188     mul = 1.0;
1189     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1190     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1191     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1192     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1193     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1194     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1195     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1196     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1197     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1198     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1199     else {
1200         syntaxerror("'%s' is not a valid color transform expression", str);
1201     }
1202     m.add = (int)(add*256);
1203     m.mul = (int)(mul*256);
1204     free(str2);
1205     return m;
1206 }
1207
1208 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1209 {
1210     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1211     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1212     MULADD r;
1213     if(a<-32768) a=-32768;
1214     if(a>32767) a=32767;
1215     if(m<-32768) m=-32768;
1216     if(m>32767) m=32767;
1217     r.add = a;
1218     r.mul = (int)m;
1219     return r;
1220 }
1221
1222 float parsePercent(char*str)
1223 {
1224     int l = strlen(str);
1225     if(!l)
1226         return 1.0;
1227     if(str[l-1]=='%') {
1228         return atoi(str)/100.0;
1229     }
1230     syntaxerror("Expression '%s' is not a percentage", str);
1231     return 0;
1232 }
1233 int isPercent(char*str)
1234 {
1235     return str[strlen(str)-1]=='%';
1236 }
1237 int parseNewSize(char*str, int size)
1238 {
1239     if(isPercent(str))
1240         return parsePercent(str)*size;
1241     else
1242         return (int)(atof(str)*20);
1243 }
1244
1245 int isColor(char*str)
1246 {
1247     RGBA c;
1248     return parseColor2(str, &c);
1249 }
1250
1251 static char* lu(map_t* args, char*name)
1252 {
1253     char* value = map_lookup(args, name);
1254     if(!value) {
1255         map_dump(args, stdout, "");
1256         syntaxerror("internal error 2: value %s should be set", name);
1257     }
1258     return value;
1259 }
1260
1261 static int c_swf(map_t*args) 
1262 {
1263     char* name = lu(args, "name");
1264     char* compressstr = lu(args, "compress");
1265     SRECT bbox = parseBox(lu(args, "bbox"));
1266     int version = parseInt(lu(args, "version"));
1267     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1268     int compress = 0;
1269     if(!strcmp(name, "!default!") || override_outputname)
1270         name = outputname;
1271     
1272     if(!strcmp(compressstr, "default"))
1273         compress = version==6;
1274     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1275         compress = 1;
1276     else if(!strcmp(compressstr, "no"))
1277         compress = 0;
1278     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1279
1280     s_swf(name, bbox, version, fps, compress);
1281     return 0;
1282 }
1283 int isRelative(char*str)
1284 {
1285     return !strncmp(str, "<plus>", 6) ||
1286            !strncmp(str, "<minus>", 7);
1287 }
1288 char* getOffset(char*str)
1289 {
1290     if(!strncmp(str, "<plus>", 6))
1291         return str+6;
1292     if(!strncmp(str, "<minus>", 7))
1293         return str+7;
1294     syntaxerror("internal error (347)");
1295     return 0;
1296 }
1297 int getSign(char*str)
1298 {
1299     if(!strncmp(str, "<plus>", 6))
1300         return 1;
1301     if(!strncmp(str, "<minus>", 7))
1302         return -1;
1303     syntaxerror("internal error (348)");
1304     return 0;
1305 }
1306 static dictionary_t points;
1307 static mem_t mpoints;
1308 int points_initialized = 0;
1309
1310 SPOINT getPoint(SRECT r, char*name)
1311 {
1312     int l=0;
1313     if(!strcmp(name, "center")) {
1314         SPOINT p;
1315         p.x = (r.xmin + r.xmax)/2;
1316         p.y = (r.ymin + r.ymax)/2;
1317         return p;
1318     }
1319
1320     if(points_initialized)
1321         l = (int)dictionary_lookup(&points, name);
1322     if(l==0) {
1323         syntaxerror("Invalid point: \"%s\".", name);
1324     }
1325     l--;
1326     return *(SPOINT*)&mpoints.buffer[l];
1327 }
1328 static int c_gradient(map_t*args) 
1329 {
1330     return 0;
1331 }
1332 static int c_point(map_t*args) 
1333 {
1334     char*name = lu(args, "name");
1335     int pos;
1336     string_t s1;
1337     SPOINT p;
1338     if(!points_initialized) {
1339         dictionary_init(&points);
1340         mem_init(&mpoints);
1341         points_initialized = 1;
1342     }
1343     p.x = parseTwip(lu(args, "x"));
1344     p.y = parseTwip(lu(args, "y"));
1345     pos = mem_put(&mpoints, &p, sizeof(p));
1346     string_set(&s1, name);
1347     pos++;
1348     dictionary_put(&points, s1, (void*)pos);
1349     return 0;
1350 }
1351 static int c_play(map_t*args) 
1352 {
1353     char*name = lu(args, "sound");
1354     char*loop = lu(args, "loop");
1355     char*nomultiple = lu(args, "nomultiple");
1356     int nm = 0;
1357     if(!strcmp(nomultiple, "nomultiple"))
1358         nm = 1;
1359     else
1360         nm = parseInt(nomultiple);
1361
1362     s_playsound(name, parseInt(loop), nm, 0);
1363     return 0;
1364 }
1365
1366 static int c_stop(map_t*args) 
1367 {
1368     char*name = lu(args, "sound");
1369     s_playsound(name, 0,0,1);
1370     return 0;
1371 }
1372
1373 static int c_placement(map_t*args, int type)
1374 {
1375     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1376     char*character = 0;
1377
1378     char* luminancestr = lu(args, "luminance");
1379     char* scalestr = lu(args, "scale");
1380     char* scalexstr = lu(args, "scalex");
1381     char* scaleystr = lu(args, "scaley");
1382     char* rotatestr = lu(args, "rotate");
1383     char* shearstr = lu(args, "shear");
1384     char* xstr="", *pivotstr="";
1385     char* ystr="", *anglestr="";
1386     char*above = lu(args, "above"); /*FIXME*/
1387     char*below = lu(args, "below");
1388     char* rstr = lu(args, "red");
1389     char* gstr = lu(args, "green");
1390     char* bstr = lu(args, "blue");
1391     char* astr = lu(args, "alpha");
1392     char* pinstr = lu(args, "pin");
1393     MULADD r,g,b,a;
1394     float oldwidth;
1395     float oldheight;
1396     SRECT oldbbox;
1397     MULADD luminance;
1398     parameters_t p;
1399
1400     if(type==5) {
1401         pivotstr = lu(args, "pivot");
1402         anglestr = lu(args, "angle");
1403     } else {
1404         xstr = lu(args, "x");
1405         ystr = lu(args, "y");
1406     }
1407     if(luminancestr[0])
1408         luminance = parseMulAdd(luminancestr);
1409     else {
1410         luminance.add = 0;
1411         luminance.mul = 256;
1412     }
1413
1414     if(scalestr[0]) {
1415         if(scalexstr[0]||scaleystr[0])
1416             syntaxerror("scalex/scaley and scale cannot both be set");
1417         scalexstr = scaleystr = scalestr;
1418     }
1419     
1420     if(type == 0 || type == 4)  {
1421         // put or startclip
1422         character = lu(args, "character");
1423         parameters_clear(&p);
1424     } else {
1425         p = s_getParameters(instance);
1426     }
1427
1428     /* x,y position */
1429     if(xstr[0]) {
1430         if(isRelative(xstr)) {
1431             if(type == 0 || type == 4)
1432                 syntaxerror("relative x values not allowed for initial put or startclip");
1433             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1434         } else {
1435             p.x = parseTwip(xstr);
1436         }
1437     }
1438     if(ystr[0]) {
1439         if(isRelative(ystr)) {
1440             if(type == 0 || type == 4)
1441                 syntaxerror("relative y values not allowed for initial put or startclip");
1442             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1443         } else {
1444             p.y = parseTwip(ystr);
1445         }
1446     }
1447
1448     /* scale, scalex, scaley */
1449     if(character) {
1450         oldbbox = s_getCharBBox(character);
1451     } else {
1452         oldbbox = s_getInstanceBBox(instance);
1453     }
1454     oldwidth = oldbbox.xmax - oldbbox.xmin;
1455     oldheight = oldbbox.ymax - oldbbox.ymin;
1456     if(scalexstr[0]) {
1457         if(oldwidth==0) p.scalex = 1.0;
1458         else {      
1459             if(scalexstr[0])
1460                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1461         }
1462     }
1463     if(scaleystr[0]) {
1464         if(oldheight==0) p.scaley = 1.0;
1465         else {
1466             if(scaleystr[0])
1467                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1468         }
1469     }
1470    
1471     /* rotation */
1472     if(rotatestr[0]) {
1473         if(isRelative(rotatestr)) {
1474             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1475         } else {
1476             p.rotate = parseFloat(rotatestr);
1477         }
1478     }
1479
1480     /* shearing */
1481     if(shearstr[0]) {
1482         if(isRelative(shearstr)) {
1483             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1484         } else {
1485             p.shear = parseFloat(shearstr);
1486         }
1487     }
1488
1489     if(pivotstr[0]) {
1490         if(isPoint(pivotstr)) 
1491             p.pivot = parsePoint(pivotstr);
1492         else 
1493             p.pivot = getPoint(oldbbox, pivotstr);
1494     }
1495     if(pinstr[0]) {
1496         if(isPoint(pinstr))
1497             p.pin = parsePoint(pinstr);
1498         else
1499             p.pin = getPoint(oldbbox, pinstr);
1500     }
1501         
1502     /* color transform */
1503
1504     if(rstr[0] || luminancestr[0]) {
1505         MULADD r;
1506         if(rstr[0])
1507             r = parseMulAdd(rstr);
1508         else {
1509             r.add = p.cxform.r0;
1510             r.mul = p.cxform.r1;
1511         }
1512         r = mergeMulAdd(r, luminance);
1513         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1514     }
1515     if(gstr[0] || luminancestr[0]) {
1516         MULADD g;
1517         if(gstr[0])
1518             g = parseMulAdd(gstr);
1519         else {
1520             g.add = p.cxform.g0;
1521             g.mul = p.cxform.g1;
1522         }
1523         g = mergeMulAdd(g, luminance);
1524         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1525     }
1526     if(bstr[0] || luminancestr[0]) {
1527         MULADD b;
1528         if(bstr[0])
1529             b = parseMulAdd(bstr);
1530         else {
1531             b.add = p.cxform.b0;
1532             b.mul = p.cxform.b1;
1533         }
1534         b = mergeMulAdd(b, luminance);
1535         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1536     }
1537     if(astr[0]) {
1538         MULADD a = parseMulAdd(astr);
1539         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1540     }
1541
1542     if(type == 0)
1543         s_put(instance, character, p);
1544     if(type == 1)
1545         s_change(instance, p);
1546     if(type == 2)
1547         s_qchange(instance, p);
1548     if(type == 3)
1549         s_jump(instance, p);
1550     if(type == 4)
1551         s_startclip(instance, character, p);
1552     return 0;
1553 }
1554 static int c_put(map_t*args) 
1555 {
1556     c_placement(args, 0);
1557     return 0;
1558 }
1559 static int c_change(map_t*args) 
1560 {
1561     c_placement(args, 1);
1562     return 0;
1563 }
1564 static int c_qchange(map_t*args) 
1565 {
1566     c_placement(args, 2);
1567     return 0;
1568 }
1569 static int c_arcchange(map_t*args) 
1570 {
1571     c_placement(args, 2);
1572     return 0;
1573 }
1574 static int c_jump(map_t*args) 
1575 {
1576     c_placement(args, 3);
1577     return 0;
1578 }
1579 static int c_startclip(map_t*args) 
1580 {
1581     c_placement(args, 4);
1582     return 0;
1583 }
1584 static int c_del(map_t*args) 
1585 {
1586     char*instance = lu(args, "name");
1587     s_delinstance(instance);
1588     return 0;
1589 }
1590 static int c_end(map_t*args) 
1591 {
1592     s_end();
1593     return 0;
1594 }
1595 static int c_sprite(map_t*args) 
1596 {
1597     char* name = lu(args, "name");
1598     s_sprite(name);
1599     return 0;
1600 }
1601 static int c_frame(map_t*args) 
1602 {
1603     char*framestr = lu(args, "n");
1604     int frame;
1605     if(isRelative(framestr)) {
1606         frame = s_getframe();
1607         if(getSign(framestr)<0)
1608             syntaxerror("relative frame expressions must be positive");
1609         frame += parseInt(getOffset(framestr));
1610     }
1611     else {
1612         frame = parseInt(framestr);
1613         if(s_getframe() >= frame
1614                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1615             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1616     }
1617     s_frame(frame);
1618     return 0;
1619 }
1620 static int c_primitive(map_t*args) 
1621 {
1622     char*name = lu(args, "name");
1623     char*command = lu(args, "commandname");
1624     int width=0, height=0, r=0;
1625     int linewidth = parseTwip(lu(args, "line"));
1626     char*colorstr = lu(args, "color");
1627     RGBA color = parseColor(colorstr);
1628     char*fillstr = lu(args, "fill");
1629     int dofill = 1;
1630     int type=0;
1631     char* font;
1632     char* text;
1633     RGBA fill;
1634     if(!strcmp(command, "circle"))
1635         type = 1;
1636     else if(!strcmp(command, "textshape"))
1637         type = 2;
1638    
1639     if(type==0) {
1640         width = parseTwip(lu(args, "width"));
1641         height = parseTwip(lu(args, "height"));
1642     } else if (type==1) {
1643         r = parseTwip(lu(args, "r"));
1644     } else if (type==2) {
1645         text = lu(args, "text");
1646         font = lu(args, "font");
1647     }
1648
1649     if(!strcmp(fillstr, "fill"))
1650         fillstr = colorstr;
1651     if(!strcmp(fillstr, "none"))
1652         dofill = 0;
1653     if(width<0 || height<0 || linewidth<0 || r<0)
1654         syntaxerror("values width, height, line, r must be positive");
1655     if(!dofill || isColor(fillstr)) {
1656         if(dofill) 
1657             fill = parseColor(fillstr);
1658     } else {
1659         /* FIXME - texture fill */
1660         fill.r = fill.g = 0;
1661         fill.b = fill.a = 255;
1662         warning("texture fill not supported yet. Filling with black.");
1663     }
1664     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1665     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1666     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1667     return 0;
1668 }
1669
1670 static int c_shape(map_t*args) 
1671 {
1672     char*name = lu(args, "name");
1673     char*filename = lu(args, "filename");
1674     s_shape(name, filename);
1675     return 0;
1676 }
1677
1678 static int c_font(map_t*args) 
1679 {
1680     char*name = lu(args, "name");
1681     char*filename = lu(args, "filename");
1682     s_font(name, filename);
1683     return 0;
1684 }
1685
1686 static int c_sound(map_t*args) 
1687 {
1688     char*name = lu(args, "name");
1689     char*filename = lu(args, "filename");
1690     s_sound(name, filename);
1691     return 0;
1692 }
1693
1694 static int c_text(map_t*args) 
1695 {
1696     char*name = lu(args, "name");
1697     char*text = lu(args, "text");
1698     char*font = lu(args, "font");
1699     float size = parsePercent(lu(args, "size"));
1700     RGBA color = parseColor(lu(args, "color"));
1701     s_text(name, font, text, (int)(size*100), color);
1702     return 0;
1703 }
1704
1705 static int c_soundtrack(map_t*args) 
1706 {
1707     return 0;
1708 }
1709
1710 int fakechar(map_t*args)
1711 {
1712     char*name = lu(args, "name");
1713     s_box(name, 0, 0, black, 20, black, 0);
1714     return 0;
1715 }
1716
1717 static int c_egon(map_t*args) {return fakechar(args);}
1718 static int c_button(map_t*args) {return fakechar(args);}
1719 static int c_edittext(map_t*args) {return fakechar(args);}
1720
1721 static int c_morphshape(map_t*args) {return fakechar(args);}
1722 static int c_image(map_t*args) {return fakechar(args);}
1723 static int c_movie(map_t*args) {return fakechar(args);}
1724
1725 static int c_buttonsounds(map_t*args) {return 0;}
1726 static int c_buttonput(map_t*args) {return 0;}
1727 static int c_texture(map_t*args) {return 0;}
1728 static int c_action(map_t*args) {return 0;}
1729
1730 static struct {
1731     char*command;
1732     command_func_t* func;
1733     char*arguments;
1734 } arguments[] =
1735 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1736  {"frame", c_frame, "n=<plus>1"},
1737
1738     // "import" type stuff
1739  {"shape", c_shape, "name filename"},
1740  {"morphshape", c_morphshape, "name start end"},
1741  {"jpeg", c_image, "name filename quality=80%"},
1742  {"png", c_image, "name filename"},
1743  {"movie", c_movie, "name filename"},
1744  {"sound", c_sound, "name filename"},
1745  {"font", c_font, "name filename"},
1746  {"soundtrack", c_soundtrack, "filename"},
1747
1748     // generators of primitives
1749
1750  {"point", c_point, "name x=0 y=0"},
1751  {"gradient", c_gradient, "name"},
1752
1753     // character generators
1754  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1755  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1756  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1757  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1758  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1759  {"text", c_text, "name text font size=100% color=white"},
1760  {"edittext", c_edittext, "name font size width height text="" color=black maxlength=0 variable="" @password=0 @wordwrap=0 @multiline=0 @html=0 @noselect=0 @readonly=0"},
1761  
1762  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1763
1764     // control tags
1765  {"play", c_play, "sound loop=0 @nomultiple=0"},
1766  {"stop", c_stop, "sound"},
1767
1768     // object placement tags
1769  {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1770  {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1771  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1772  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1773  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1774  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1775  {"del", c_del, "name"},
1776     // virtual object placement
1777  {"buttonput", c_buttonput, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex=100% scaley=100% shear=0 rotate=0 above= below="},
1778  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1779
1780     // commands which start a block
1781 //startclip (see above)
1782  {"sprite", c_sprite, "name"},
1783  {"action", c_action, ""},
1784
1785  {"end", c_end, ""}
1786 };
1787
1788
1789 static map_t parseArguments(char*command, char*pattern)
1790 {
1791     char*x;
1792     char*d,*e;
1793    
1794     string_t name[64];
1795     string_t value[64];
1796     int set[64];
1797     int isboolean[64];
1798     int pos;
1799     int len;
1800     int t;
1801     string_t t1,t2;
1802     map_t result;
1803     map_init(&result);
1804
1805     string_set(&t1, "commandname");
1806     string_set(&t2, command);
1807     map_put(&result, t1, t2);
1808
1809     if(!pattern || !*pattern)
1810         return result;
1811
1812     x = pattern;
1813
1814     pos = 0;
1815
1816     if(!strncmp("<i> ", x, 3)) {
1817         readToken();
1818         if(type == COMMAND || type == LABEL) {
1819             pushBack();
1820             syntaxerror("character name expected");
1821         }
1822         name[pos].str = "instance";
1823         name[pos].len = 8;
1824         value[pos].str = text;
1825         value[pos].len = strlen(text);
1826         set[pos] = 1;
1827         pos++;
1828
1829         if(type == ASSIGNMENT)
1830             readToken();
1831
1832         name[pos].str = "character";
1833         name[pos].len = 9;
1834         value[pos].str = text;
1835         value[pos].len = strlen(text);
1836         set[pos] = 1;
1837         pos++;
1838
1839         x+=4;
1840     }
1841
1842     while(*x) {
1843         isboolean[pos] = (x[0] =='@');
1844         if(isboolean[pos])
1845             x++;
1846
1847         d = strchr(x, ' ');
1848         e = strchr(x, '=');
1849         if(!d)
1850             d=&x[strlen(x)];
1851         set[pos] = 0;
1852
1853         if(!e || d<e) { 
1854             // no default
1855             name[pos].str = x;
1856             name[pos].len = d-x;
1857             value[pos].str = 0;
1858             value[pos].len = 0;
1859         } else {
1860             name[pos].str = x;
1861             name[pos].len = e-x;
1862             value[pos].str = e+1;
1863             value[pos].len = d-e-1;
1864         }
1865         pos++;
1866         if(!*d) break;
1867         x=d+1;
1868     }
1869     len = pos;
1870
1871 /*    for(t=0;t<len;t++) {
1872         printf("(%d) %s=%s %s\n", t, strdup_n(name[t], namelen[t]), strdup_n(value[t], valuelen[t]),
1873                 isboolean[t]?"(boolean)":"");
1874     }*/
1875
1876     while(1) {
1877         readToken();
1878         if(type == LABEL || type == COMMAND) {
1879             pushBack();
1880             break;
1881         }
1882
1883         // first, search for boolean arguments
1884         for(pos=0;pos<len;pos++)
1885         {
1886             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1887                 set[pos] = 1;
1888                 if(type == ASSIGNMENT)
1889                     readToken();
1890                 value[pos].str = text;
1891                 value[pos].len = strlen(text);
1892                 /*printf("setting boolean parameter %s (to %s)\n",
1893                         strdup_n(name[pos], namelen[pos]),
1894                         strdup_n(value[pos], valuelen[pos]));*/
1895                 break;
1896             }
1897         }
1898
1899         // second, search for normal arguments
1900         if(pos==len)
1901         for(pos=0;pos<len;pos++)
1902         {
1903             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1904                (type != ASSIGNMENT && !set[pos])) {
1905                 if(set[pos]) {
1906                     syntaxerror("value %s set twice (old value:%s)", text, strdup_n(value[pos].str, value[pos].len));
1907                 }
1908                 if(type == ASSIGNMENT)
1909                     readToken();
1910                 set[pos] = 1;
1911                 value[pos].str = text;
1912                 value[pos].len = strlen(text);
1913 #if 0//def DEBUG
1914                 printf("setting parameter %s (to %s)\n",
1915                         strdup_n(name[pos].str, name[pos].len),
1916                         strdup_n(value[pos].str, value[pos].len));
1917 #endif
1918                 break;
1919             }
1920         }
1921         if(pos==len) {
1922             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1923         }
1924     }
1925 #if 0//def DEBUG
1926     for(t=0;t<len;t++) {
1927         printf("%s=%s\n", strdup_n(name[t].str, name[t].len), strdup_n(value[t].str, value[t].len));
1928     }
1929 #endif
1930     for(t=0;t<len;t++) {
1931         if(value[t].str && value[t].str[0] == '*') {
1932             //relative default- take value from some other parameter
1933             int s;
1934             for(s=0;s<len;s++) {
1935                 if(value[s].len == value[t].len-1 &&
1936                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1937                     value[t].str = value[s].str;
1938             }
1939         }
1940         if(value[t].str == 0) {
1941             pushBack();
1942             syntaxerror("value for parameter %s missing (no default)", strdup_n(name[t].str, name[t].len));
1943         }
1944     }
1945
1946     /* ok, now construct the dictionary from the parameters */
1947
1948     for(t=0;t<len;t++) 
1949     {
1950         map_put(&result, name[t], value[t]);
1951     }
1952     return result;
1953 }
1954 static void parseArgumentsForCommand(char*command)
1955 {
1956     int t;
1957     map_t args;
1958     int nr = -1;
1959     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1960         if(!strcmp(arguments[t].command, command)) {
1961             args = parseArguments(command, arguments[t].arguments);
1962             nr = t;
1963         }
1964     }
1965     if(nr<0)
1966         syntaxerror("command %s not known", command);
1967
1968 #ifdef DEBUG
1969     printf(".%s\n", command);fflush(stdout);
1970     map_dump(&args, stdout, "\t");fflush(stdout);
1971 #endif
1972
1973     (*arguments[nr].func)(&args);
1974
1975     if(!strcmp(command, "button") ||
1976        !strcmp(command, "action")) {
1977         while(1) {
1978             readToken();
1979             if(type == COMMAND) {
1980                 if(!strcmp(text, "end"))
1981                     break;
1982                 else {
1983                     pushBack();
1984                     break;
1985                 }
1986             }
1987         }
1988     }
1989
1990     map_clear(&args);
1991     return;
1992 }
1993
1994 int main (int argc,char ** argv)
1995
1996     int t;
1997     processargs(argc, argv);
1998     initLog(0,-1,0,0,-1,verbose);
1999
2000     if(!filename) {
2001         args_callback_usage(argv[0]);
2002         exit(1);
2003     }
2004     file = generateTokens(filename);
2005     if(!file) {
2006         printf("parser returned error.\n");
2007         return 1;
2008     }
2009     pos=0;
2010
2011     t=0;
2012
2013     while(!noMoreTokens()) {
2014         readToken();
2015         if(type != COMMAND)
2016             syntaxerror("command expected");
2017         parseArgumentsForCommand(text);
2018     }
2019
2020     s_close();
2021     freeTokens(file);
2022
2023     return 0;
2024 }
2025