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