added O_BINARY
[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 "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 = strchr(str, '.');
1055     if(!dot) {
1056         int l=strlen(str);
1057         int t;
1058         return parseInt(str)*20;
1059     } else {
1060         int l=strlen(++dot);
1061         char*s;
1062         for(s=str;s<dot-1;s++)
1063             if(*s<'0' || *s>'9')
1064                 syntaxerror("Not a coordinate: \"%s\"", str);
1065         for(s=dot;*s;s++) {
1066             if(*s<'0' || *s>'9')
1067                 syntaxerror("Not a coordinate: \"%s\"", str);
1068         }
1069         if(l>2 || (l==2 && (dot[1]!='0' || dot[1]!='5'))) {
1070             warning("precision loss: %s converted to twip", str);
1071             dot[2] = 0;
1072             l=2;
1073         }
1074         if(l==0)
1075             return atoi(str)*20;
1076         if(l==1)
1077             return atoi(str)*20+atoi(dot)*2;
1078         if(l==2)
1079             return atoi(str)*20+atoi(dot)/5;
1080     }
1081     return 0;
1082 }
1083
1084 int isPoint(char*str)
1085 {
1086     if(strchr(str, '('))
1087         return 1;
1088     else
1089         return 0;
1090 }
1091
1092 SPOINT parsePoint(char*str)
1093 {
1094     SPOINT p;
1095     char tmp[80];
1096     int l = strlen(str);
1097     char*comma = strchr(str, ',');
1098     if(str[0]!='(' || str[l-1]!=')' || !comma || l>70)
1099         syntaxerror("\"%s\" is not a valid point of the form (x,y)", str);
1100     strncpy(tmp, str+1, comma-(str+1));tmp[comma-(str+1)]=0;
1101     p.x = parseTwip(tmp);
1102     strncpy(tmp, comma+1, l-1-(comma+1-str));tmp[l-1-(comma+1-str)]=0;
1103     p.y = parseTwip(tmp);
1104     return p;
1105 }
1106
1107 int parseColor2(char*str, RGBA*color)
1108 {
1109     int l = strlen(str);
1110     int r,g,b,a;
1111     int t;
1112
1113     struct {unsigned char r,g,b;char*name;} colors[] =
1114     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1115     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1116     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1117     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1118     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1119     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1120     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1121     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1122     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1123     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1124     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1125     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1126
1127     a=255;r=g=b=0;
1128
1129     if(str[0]=='#' && (l==7 || l==9)) {
1130         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1131             return 0;
1132         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1133             return 0;
1134         color->r = r; color->g = g; color->b = b; color->a = a;
1135         return 1;
1136     }
1137     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1138         if(!strcmp(str, colors[t].name)) {
1139             r = colors[t].r;
1140             g = colors[t].g;
1141             b = colors[t].b;
1142             a = 255;
1143             color->r = r; color->g = g; color->b = b; color->a = a;
1144             return 1;
1145         }
1146     return 0;
1147
1148 }
1149 RGBA parseColor(char*str)
1150 {
1151     RGBA c;
1152     if(!parseColor2(str, &c))
1153         syntaxerror("Expression '%s' is not a color", str);
1154     return c;
1155 }
1156
1157 typedef struct _muladd {
1158     S16 mul;
1159     S16 add;
1160 } MULADD;
1161
1162 MULADD parseMulAdd(char*str)
1163 {
1164     float add, mul;
1165     char* str2 = (char*)malloc(strlen(str)+5);
1166     int i;
1167     MULADD m;
1168     strcpy(str2, str);
1169     strcat(str2, " 0");
1170     add = 0;
1171     mul = 1.0;
1172     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1173     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1174     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1175     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1176     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1177     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1178     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1179     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1180     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1181     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1182     else {
1183         syntaxerror("'%s' is not a valid color transform expression", str);
1184     }
1185     m.add = (int)(add*256);
1186     m.mul = (int)(mul*256);
1187     free(str2);
1188     return m;
1189 }
1190
1191 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1192 {
1193     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1194     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1195     MULADD r;
1196     if(a<-32768) a=-32768;
1197     if(a>32767) a=32767;
1198     if(m<-32768) m=-32768;
1199     if(m>32767) m=32767;
1200     r.add = a;
1201     r.mul = (int)m;
1202     return r;
1203 }
1204
1205 float parsePercent(char*str)
1206 {
1207     int l = strlen(str);
1208     if(!l)
1209         return 1.0;
1210     if(str[l-1]=='%') {
1211         return atoi(str)/100.0;
1212     }
1213     syntaxerror("Expression '%s' is not a percentage", str);
1214     return 0;
1215 }
1216 int isPercent(char*str)
1217 {
1218     return str[strlen(str)-1]=='%';
1219 }
1220 int parseNewSize(char*str, int size)
1221 {
1222     if(isPercent(str))
1223         return parsePercent(str)*size;
1224     else
1225         return (int)(atof(str)*20);
1226 }
1227
1228 int isColor(char*str)
1229 {
1230     RGBA c;
1231     return parseColor2(str, &c);
1232 }
1233
1234 static char* lu(map_t* args, char*name)
1235 {
1236     char* value = map_lookup(args, name);
1237     if(!value) {
1238         map_dump(args, stdout, "");
1239         syntaxerror("internal error 2: value %s should be set", name);
1240     }
1241     return value;
1242 }
1243
1244 static int c_swf(map_t*args) 
1245 {
1246     char* name = lu(args, "name");
1247     char* compressstr = lu(args, "compress");
1248     SRECT bbox = parseBox(lu(args, "bbox"));
1249     int version = parseInt(lu(args, "version"));
1250     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1251     int compress = 0;
1252     if(!strcmp(name, "!default!") || override_outputname)
1253         name = outputname;
1254     
1255     if(!strcmp(compressstr, "default"))
1256         compress = version==6;
1257     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1258         compress = 1;
1259     else if(!strcmp(compressstr, "no"))
1260         compress = 0;
1261     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1262
1263     s_swf(name, bbox, version, fps, compress);
1264     return 0;
1265 }
1266 int isRelative(char*str)
1267 {
1268     return !strncmp(str, "<plus>", 6) ||
1269            !strncmp(str, "<minus>", 7);
1270 }
1271 char* getOffset(char*str)
1272 {
1273     if(!strncmp(str, "<plus>", 6))
1274         return str+6;
1275     if(!strncmp(str, "<minus>", 7))
1276         return str+7;
1277     syntaxerror("internal error (347)");
1278     return 0;
1279 }
1280 int getSign(char*str)
1281 {
1282     if(!strncmp(str, "<plus>", 6))
1283         return 1;
1284     if(!strncmp(str, "<minus>", 7))
1285         return -1;
1286     syntaxerror("internal error (348)");
1287     return 0;
1288 }
1289 static dictionary_t points;
1290 static mem_t mpoints;
1291 int points_initialized = 0;
1292
1293 SPOINT getPoint(SRECT r, char*name)
1294 {
1295     int l=0;
1296     if(!strcmp(name, "center")) {
1297         SPOINT p;
1298         p.x = (r.xmin + r.xmax)/2;
1299         p.y = (r.ymin + r.ymax)/2;
1300         return p;
1301     }
1302
1303     if(points_initialized)
1304         l = (int)dictionary_lookup(&points, name);
1305     if(l==0) {
1306         syntaxerror("Invalid point: \"%s\".", name);
1307     }
1308     l--;
1309     return *(SPOINT*)&mpoints.buffer[l];
1310 }
1311 static int c_point(map_t*args) 
1312 {
1313     char*name = lu(args, "name");
1314     int pos;
1315     string_t s1;
1316     SPOINT p;
1317     if(!points_initialized) {
1318         dictionary_init(&points);
1319         mem_init(&mpoints);
1320         points_initialized = 1;
1321     }
1322     p.x = parseTwip(lu(args, "x"));
1323     p.y = parseTwip(lu(args, "y"));
1324     pos = mem_put(&mpoints, &p, sizeof(p));
1325     string_set(&s1, name);
1326     pos++;
1327     dictionary_put(&points, s1, (void*)pos);
1328     return 0;
1329 }
1330 static int c_play(map_t*args) 
1331 {
1332     char*name = lu(args, "sound");
1333     char*loop = lu(args, "loop");
1334     char*nomultiple = lu(args, "nomultiple");
1335     int nm = 0;
1336     if(!strcmp(nomultiple, "nomultiple"))
1337         nm = 1;
1338     else
1339         nm = parseInt(nomultiple);
1340
1341     s_playsound(name, parseInt(loop), nm, 0);
1342     return 0;
1343 }
1344
1345 static int c_stop(map_t*args) 
1346 {
1347     char*name = lu(args, "sound");
1348     s_playsound(name, 0,0,1);
1349     return 0;
1350 }
1351
1352 static int c_placement(map_t*args, int type)
1353 {
1354     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1355     char*character = 0;
1356
1357     char* luminancestr = lu(args, "luminance");
1358     char* scalestr = lu(args, "scale");
1359     char* scalexstr = lu(args, "scalex");
1360     char* scaleystr = lu(args, "scaley");
1361     char* rotatestr = lu(args, "rotate");
1362     char* shearstr = lu(args, "shear");
1363     char* xstr="", *pivotstr="";
1364     char* ystr="", *anglestr="";
1365     char*above = lu(args, "above"); /*FIXME*/
1366     char*below = lu(args, "below");
1367     char* rstr = lu(args, "red");
1368     char* gstr = lu(args, "green");
1369     char* bstr = lu(args, "blue");
1370     char* astr = lu(args, "alpha");
1371     char* pinstr = lu(args, "pin");
1372     MULADD r,g,b,a;
1373     float oldwidth;
1374     float oldheight;
1375     SRECT oldbbox;
1376     MULADD luminance;
1377     parameters_t p;
1378
1379     if(type==5) {
1380         pivotstr = lu(args, "pivot");
1381         anglestr = lu(args, "angle");
1382     } else {
1383         xstr = lu(args, "x");
1384         ystr = lu(args, "y");
1385     }
1386     if(luminancestr[0])
1387         luminance = parseMulAdd(luminancestr);
1388     else {
1389         luminance.add = 0;
1390         luminance.mul = 256;
1391     }
1392
1393     if(scalestr[0]) {
1394         if(scalexstr[0]||scaleystr[0])
1395             syntaxerror("scalex/scaley and scale cannot both be set");
1396         scalexstr = scaleystr = scalestr;
1397     }
1398     
1399     if(type == 0 || type == 4)  {
1400         // put or startclip
1401         character = lu(args, "character");
1402         parameters_clear(&p);
1403     } else {
1404         p = s_getParameters(instance);
1405     }
1406
1407     /* x,y position */
1408     if(xstr[0]) {
1409         if(isRelative(xstr)) {
1410             if(type == 0 || type == 4)
1411                 syntaxerror("relative x values not allowed for initial put or startclip");
1412             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1413         } else {
1414             p.x = parseTwip(xstr);
1415         }
1416     }
1417     if(ystr[0]) {
1418         if(isRelative(ystr)) {
1419             if(type == 0 || type == 4)
1420                 syntaxerror("relative y values not allowed for initial put or startclip");
1421             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1422         } else {
1423             p.y = parseTwip(ystr);
1424         }
1425     }
1426
1427     /* scale, scalex, scaley */
1428     if(character) {
1429         oldbbox = s_getCharBBox(character);
1430     } else {
1431         oldbbox = s_getInstanceBBox(instance);
1432     }
1433     oldwidth = oldbbox.xmax - oldbbox.xmin;
1434     oldheight = oldbbox.ymax - oldbbox.ymin;
1435     if(scalexstr[0]) {
1436         if(oldwidth==0) p.scalex = 1.0;
1437         else {      
1438             if(scalexstr[0])
1439                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1440         }
1441     }
1442     if(scaleystr[0]) {
1443         if(oldheight==0) p.scaley = 1.0;
1444         else {
1445             if(scaleystr[0])
1446                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1447         }
1448     }
1449    
1450     /* rotation */
1451     if(rotatestr[0]) {
1452         if(isRelative(rotatestr)) {
1453             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1454         } else {
1455             p.rotate = parseFloat(rotatestr);
1456         }
1457     }
1458
1459     /* shearing */
1460     if(shearstr[0]) {
1461         if(isRelative(shearstr)) {
1462             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1463         } else {
1464             p.shear = parseFloat(shearstr);
1465         }
1466     }
1467
1468     if(pivotstr[0]) {
1469         if(isPoint(pivotstr)) 
1470             p.pivot = parsePoint(pivotstr);
1471         else 
1472             p.pivot = getPoint(oldbbox, pivotstr);
1473     }
1474     if(pinstr[0]) {
1475         if(isPoint(pinstr))
1476             p.pin = parsePoint(pinstr);
1477         else
1478             p.pin = getPoint(oldbbox, pinstr);
1479     }
1480         
1481     /* color transform */
1482
1483     if(rstr[0] || luminancestr[0]) {
1484         MULADD r;
1485         if(rstr[0])
1486             r = parseMulAdd(rstr);
1487         else {
1488             r.add = p.cxform.r0;
1489             r.mul = p.cxform.r1;
1490         }
1491         r = mergeMulAdd(r, luminance);
1492         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1493     }
1494     if(gstr[0] || luminancestr[0]) {
1495         MULADD g;
1496         if(gstr[0])
1497             g = parseMulAdd(gstr);
1498         else {
1499             g.add = p.cxform.g0;
1500             g.mul = p.cxform.g1;
1501         }
1502         g = mergeMulAdd(g, luminance);
1503         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1504     }
1505     if(bstr[0] || luminancestr[0]) {
1506         MULADD b;
1507         if(bstr[0])
1508             b = parseMulAdd(bstr);
1509         else {
1510             b.add = p.cxform.b0;
1511             b.mul = p.cxform.b1;
1512         }
1513         b = mergeMulAdd(b, luminance);
1514         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1515     }
1516     if(astr[0]) {
1517         MULADD a = parseMulAdd(astr);
1518         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1519     }
1520
1521     if(type == 0)
1522         s_put(instance, character, p);
1523     if(type == 1)
1524         s_change(instance, p);
1525     if(type == 2)
1526         s_qchange(instance, p);
1527     if(type == 3)
1528         s_jump(instance, p);
1529     if(type == 4)
1530         s_startclip(instance, character, p);
1531     return 0;
1532 }
1533 static int c_put(map_t*args) 
1534 {
1535     c_placement(args, 0);
1536     return 0;
1537 }
1538 static int c_change(map_t*args) 
1539 {
1540     c_placement(args, 1);
1541     return 0;
1542 }
1543 static int c_qchange(map_t*args) 
1544 {
1545     c_placement(args, 2);
1546     return 0;
1547 }
1548 static int c_arcchange(map_t*args) 
1549 {
1550     c_placement(args, 2);
1551     return 0;
1552 }
1553 static int c_jump(map_t*args) 
1554 {
1555     c_placement(args, 3);
1556     return 0;
1557 }
1558 static int c_startclip(map_t*args) 
1559 {
1560     c_placement(args, 4);
1561     return 0;
1562 }
1563 static int c_del(map_t*args) 
1564 {
1565     char*instance = lu(args, "name");
1566     s_delinstance(instance);
1567     return 0;
1568 }
1569 static int c_end(map_t*args) 
1570 {
1571     s_end();
1572     return 0;
1573 }
1574 static int c_sprite(map_t*args) 
1575 {
1576     char* name = lu(args, "name");
1577     s_sprite(name);
1578     return 0;
1579 }
1580 static int c_frame(map_t*args) 
1581 {
1582     char*framestr = lu(args, "n");
1583     int frame;
1584     if(isRelative(framestr)) {
1585         frame = s_getframe();
1586         if(getSign(framestr)<0)
1587             syntaxerror("relative frame expressions must be positive");
1588         frame += parseInt(getOffset(framestr));
1589     }
1590     else {
1591         frame = parseInt(framestr);
1592         if(s_getframe() >= frame
1593                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1594             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1595     }
1596     s_frame(frame);
1597     return 0;
1598 }
1599 static int c_primitive(map_t*args) 
1600 {
1601     char*name = lu(args, "name");
1602     char*command = lu(args, "commandname");
1603     int width=0, height=0, r=0;
1604     int linewidth = parseTwip(lu(args, "line"));
1605     char*colorstr = lu(args, "color");
1606     RGBA color = parseColor(colorstr);
1607     char*fillstr = lu(args, "fill");
1608     int dofill = 1;
1609     int type=0;
1610     char* font;
1611     char* text;
1612     RGBA fill;
1613     if(!strcmp(command, "circle"))
1614         type = 1;
1615     else if(!strcmp(command, "textshape"))
1616         type = 2;
1617    
1618     if(type==0) {
1619         width = parseTwip(lu(args, "width"));
1620         height = parseTwip(lu(args, "height"));
1621     } else if (type==1) {
1622         r = parseTwip(lu(args, "r"));
1623     } else if (type==2) {
1624         text = lu(args, "text");
1625         font = lu(args, "font");
1626     }
1627
1628     if(!strcmp(fillstr, "fill"))
1629         fillstr = colorstr;
1630     if(!strcmp(fillstr, "none"))
1631         dofill = 0;
1632     if(width<0 || height<0 || linewidth<0 || r<0)
1633         syntaxerror("values width, height, line, r must be positive");
1634     if(!dofill || isColor(fillstr)) {
1635         if(dofill) 
1636             fill = parseColor(fillstr);
1637     } else {
1638         /* FIXME - texture fill */
1639         fill.r = fill.g = 0;
1640         fill.b = fill.a = 255;
1641         warning("texture fill not supported yet. Filling with black.");
1642     }
1643     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1644     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1645     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1646     return 0;
1647 }
1648
1649 static int c_shape(map_t*args) 
1650 {
1651     char*name = lu(args, "name");
1652     char*filename = lu(args, "filename");
1653     s_shape(name, filename);
1654     return 0;
1655 }
1656
1657 static int c_font(map_t*args) 
1658 {
1659     char*name = lu(args, "name");
1660     char*filename = lu(args, "filename");
1661     s_font(name, filename);
1662     return 0;
1663 }
1664
1665 static int c_sound(map_t*args) 
1666 {
1667     char*name = lu(args, "name");
1668     char*filename = lu(args, "filename");
1669     s_sound(name, filename);
1670     return 0;
1671 }
1672
1673 static int c_text(map_t*args) 
1674 {
1675     char*name = lu(args, "name");
1676     char*text = lu(args, "text");
1677     char*font = lu(args, "font");
1678     float size = parsePercent(lu(args, "size"));
1679     RGBA color = parseColor(lu(args, "color"));
1680     s_text(name, font, text, (int)(size*100), color);
1681     return 0;
1682 }
1683
1684 static int c_soundtrack(map_t*args) 
1685 {
1686     return 0;
1687 }
1688
1689 int fakechar(map_t*args)
1690 {
1691     char*name = lu(args, "name");
1692     s_box(name, 0, 0, black, 20, black, 0);
1693     return 0;
1694 }
1695
1696 static int c_egon(map_t*args) {return fakechar(args);}
1697 static int c_button(map_t*args) {return fakechar(args);}
1698 static int c_edittext(map_t*args) {return fakechar(args);}
1699
1700 static int c_morphshape(map_t*args) {return fakechar(args);}
1701 static int c_image(map_t*args) {return fakechar(args);}
1702 static int c_movie(map_t*args) {return fakechar(args);}
1703
1704 static int c_buttonsounds(map_t*args) {return 0;}
1705 static int c_buttonput(map_t*args) {return 0;}
1706 static int c_texture(map_t*args) {return 0;}
1707 static int c_action(map_t*args) {return 0;}
1708
1709 static struct {
1710     char*command;
1711     command_func_t* func;
1712     char*arguments;
1713 } arguments[] =
1714 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1715  {"frame", c_frame, "n=<plus>1"},
1716
1717     // "import" type stuff
1718  {"shape", c_shape, "name filename"},
1719  {"morphshape", c_morphshape, "name start end"},
1720  {"jpeg", c_image, "name filename quality=80%"},
1721  {"png", c_image, "name filename"},
1722  {"movie", c_movie, "name filename"},
1723  {"sound", c_sound, "name filename"},
1724  {"font", c_font, "name filename"},
1725  {"soundtrack", c_soundtrack, "filename"},
1726
1727     // character generators
1728  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1729  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1730  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1731  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1732  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1733  {"text", c_text, "name text font size=100% color=white"},
1734  {"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"},
1735  
1736  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1737
1738     // control tags
1739  {"play", c_play, "sound loop=0 @nomultiple=0"},
1740  {"stop", c_stop, "sound"},
1741
1742     // object placement tags
1743  {"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="},
1744  {"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="},
1745  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1746  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1747  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1748  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1749  {"del", c_del, "name"},
1750     // virtual object placement
1751  {"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="},
1752  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1753  {"point", c_point, "name x=0 y=0"},
1754
1755     // commands which start a block
1756 //startclip (see above)
1757  {"sprite", c_sprite, "name"},
1758  {"action", c_action, ""},
1759
1760  {"end", c_end, ""}
1761 };
1762
1763
1764 static map_t parseArguments(char*command, char*pattern)
1765 {
1766     char*x;
1767     char*d,*e;
1768    
1769     string_t name[64];
1770     string_t value[64];
1771     int set[64];
1772     int isboolean[64];
1773     int pos;
1774     int len;
1775     int t;
1776     string_t t1,t2;
1777     map_t result;
1778     map_init(&result);
1779
1780     string_set(&t1, "commandname");
1781     string_set(&t2, command);
1782     map_put(&result, t1, t2);
1783
1784     if(!pattern || !*pattern)
1785         return result;
1786
1787     x = pattern;
1788
1789     pos = 0;
1790
1791     if(!strncmp("<i> ", x, 3)) {
1792         readToken();
1793         if(type == COMMAND || type == LABEL) {
1794             pushBack();
1795             syntaxerror("character name expected");
1796         }
1797         name[pos].str = "instance";
1798         name[pos].len = 8;
1799         value[pos].str = text;
1800         value[pos].len = strlen(text);
1801         set[pos] = 1;
1802         pos++;
1803
1804         if(type == ASSIGNMENT)
1805             readToken();
1806
1807         name[pos].str = "character";
1808         name[pos].len = 9;
1809         value[pos].str = text;
1810         value[pos].len = strlen(text);
1811         set[pos] = 1;
1812         pos++;
1813
1814         x+=4;
1815     }
1816
1817     while(*x) {
1818         isboolean[pos] = (x[0] =='@');
1819         if(isboolean[pos])
1820             x++;
1821
1822         d = strchr(x, ' ');
1823         e = strchr(x, '=');
1824         if(!d)
1825             d=&x[strlen(x)];
1826         set[pos] = 0;
1827
1828         if(!e || d<e) { 
1829             // no default
1830             name[pos].str = x;
1831             name[pos].len = d-x;
1832             value[pos].str = 0;
1833             value[pos].len = 0;
1834         } else {
1835             name[pos].str = x;
1836             name[pos].len = e-x;
1837             value[pos].str = e+1;
1838             value[pos].len = d-e-1;
1839         }
1840         pos++;
1841         if(!*d) break;
1842         x=d+1;
1843     }
1844     len = pos;
1845
1846 /*    for(t=0;t<len;t++) {
1847         printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1848                 isboolean[t]?"(boolean)":"");
1849     }*/
1850
1851     while(1) {
1852         readToken();
1853         if(type == LABEL || type == COMMAND) {
1854             pushBack();
1855             break;
1856         }
1857
1858         // first, search for boolean arguments
1859         for(pos=0;pos<len;pos++)
1860         {
1861             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1862                 set[pos] = 1;
1863                 if(type == ASSIGNMENT)
1864                     readToken();
1865                 value[pos].str = text;
1866                 value[pos].len = strlen(text);
1867                 /*printf("setting boolean parameter %s (to %s)\n",
1868                         strndup(name[pos], namelen[pos]),
1869                         strndup(value[pos], valuelen[pos]));*/
1870                 break;
1871             }
1872         }
1873
1874         // second, search for normal arguments
1875         if(pos==len)
1876         for(pos=0;pos<len;pos++)
1877         {
1878             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1879                (type != ASSIGNMENT && !set[pos])) {
1880                 if(set[pos]) {
1881                     syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1882                 }
1883                 if(type == ASSIGNMENT)
1884                     readToken();
1885                 set[pos] = 1;
1886                 value[pos].str = text;
1887                 value[pos].len = strlen(text);
1888 #if 0//def DEBUG
1889                 printf("setting parameter %s (to %s)\n",
1890                         strndup(name[pos].str, name[pos].len),
1891                         strndup(value[pos].str, value[pos].len));
1892 #endif
1893                 break;
1894             }
1895         }
1896         if(pos==len) {
1897             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1898         }
1899     }
1900 #if 0//def DEBUG
1901     for(t=0;t<len;t++) {
1902         printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1903     }
1904 #endif
1905     for(t=0;t<len;t++) {
1906         if(value[t].str && value[t].str[0] == '*') {
1907             //relative default- take value from some other parameter
1908             int s;
1909             for(s=0;s<len;s++) {
1910                 if(value[s].len == value[t].len-1 &&
1911                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1912                     value[t].str = value[s].str;
1913             }
1914         }
1915         if(value[t].str == 0) {
1916             pushBack();
1917             syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1918         }
1919     }
1920
1921     /* ok, now construct the dictionary from the parameters */
1922
1923     for(t=0;t<len;t++) 
1924     {
1925         map_put(&result, name[t], value[t]);
1926     }
1927     return result;
1928 }
1929 static void parseArgumentsForCommand(char*command)
1930 {
1931     int t;
1932     map_t args;
1933     int nr = -1;
1934     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1935         if(!strcmp(arguments[t].command, command)) {
1936             args = parseArguments(command, arguments[t].arguments);
1937             nr = t;
1938         }
1939     }
1940     if(nr<0)
1941         syntaxerror("command %s not known", command);
1942
1943 #ifdef DEBUG
1944     printf(".%s\n", command);fflush(stdout);
1945     map_dump(&args, stdout, "\t");fflush(stdout);
1946 #endif
1947
1948     (*arguments[nr].func)(&args);
1949
1950     if(!strcmp(command, "button") ||
1951        !strcmp(command, "action")) {
1952         while(1) {
1953             readToken();
1954             if(type == COMMAND) {
1955                 if(!strcmp(text, "end"))
1956                     break;
1957                 else {
1958                     pushBack();
1959                     break;
1960                 }
1961             }
1962         }
1963     }
1964
1965     map_clear(&args);
1966     return;
1967 }
1968
1969 int main (int argc,char ** argv)
1970
1971     int t;
1972     processargs(argc, argv);
1973     initLog(0,-1,0,0,-1,verbose);
1974
1975     if(!filename) {
1976         args_callback_usage(argv[0]);
1977         exit(1);
1978     }
1979     file = generateTokens(filename);
1980     if(!file) {
1981         printf("parser returned error.\n");
1982         return 1;
1983     }
1984     pos=0;
1985
1986     t=0;
1987
1988     while(!noMoreTokens()) {
1989         readToken();
1990         if(type != COMMAND)
1991             syntaxerror("command expected");
1992         parseArgumentsForCommand(text);
1993     }
1994
1995     s_close();
1996     freeTokens(file);
1997
1998     return 0;
1999 }
2000