* executables get stripped now
[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, 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);
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.multiple = !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
1336     s_playsound(name, parseInt(loop), parseInt(nomultiple), 0);
1337     return 0;
1338 }
1339
1340 static int c_stop(map_t*args) 
1341 {
1342     char*name = lu(args, "sound");
1343     s_playsound(name, 0,0,1);
1344     return 0;
1345 }
1346
1347 static int c_placement(map_t*args, int type)
1348 {
1349     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1350     char*character = 0;
1351
1352     char* luminancestr = lu(args, "luminance");
1353     char* scalestr = lu(args, "scale");
1354     char* scalexstr = lu(args, "scalex");
1355     char* scaleystr = lu(args, "scaley");
1356     char* rotatestr = lu(args, "rotate");
1357     char* shearstr = lu(args, "shear");
1358     char* xstr="", *pivotstr="";
1359     char* ystr="", *anglestr="";
1360     char*above = lu(args, "above"); /*FIXME*/
1361     char*below = lu(args, "below");
1362     char* rstr = lu(args, "red");
1363     char* gstr = lu(args, "green");
1364     char* bstr = lu(args, "blue");
1365     char* astr = lu(args, "alpha");
1366     char* pinstr = lu(args, "pin");
1367     MULADD r,g,b,a;
1368     float oldwidth;
1369     float oldheight;
1370     SRECT oldbbox;
1371     MULADD luminance;
1372     parameters_t p;
1373
1374     if(type==5) {
1375         pivotstr = lu(args, "pivot");
1376         anglestr = lu(args, "angle");
1377     } else {
1378         xstr = lu(args, "x");
1379         ystr = lu(args, "y");
1380     }
1381     if(luminancestr[0])
1382         luminance = parseMulAdd(luminancestr);
1383     else {
1384         luminance.add = 0;
1385         luminance.mul = 256;
1386     }
1387
1388     if(scalestr[0]) {
1389         if(scalexstr[0]||scaleystr[0])
1390             syntaxerror("scalex/scaley and scale cannot both be set");
1391         scalexstr = scaleystr = scalestr;
1392     }
1393     
1394     if(type == 0 || type == 4)  {
1395         // put or startclip
1396         character = lu(args, "character");
1397         parameters_clear(&p);
1398     } else {
1399         p = s_getParameters(instance);
1400     }
1401
1402     /* x,y position */
1403     if(xstr[0]) {
1404         if(isRelative(xstr)) {
1405             if(type == 0 || type == 4)
1406                 syntaxerror("relative x values not allowed for initial put or startclip");
1407             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1408         } else {
1409             p.x = parseTwip(xstr);
1410         }
1411     }
1412     if(ystr[0]) {
1413         if(isRelative(ystr)) {
1414             if(type == 0 || type == 4)
1415                 syntaxerror("relative y values not allowed for initial put or startclip");
1416             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1417         } else {
1418             p.y = parseTwip(ystr);
1419         }
1420     }
1421
1422     /* scale, scalex, scaley */
1423     if(character) {
1424         oldbbox = s_getCharBBox(character);
1425     } else {
1426         oldbbox = s_getInstanceBBox(instance);
1427     }
1428     oldwidth = oldbbox.xmax - oldbbox.xmin;
1429     oldheight = oldbbox.ymax - oldbbox.ymin;
1430     if(scalexstr[0]) {
1431         if(oldwidth==0) p.scalex = 1.0;
1432         else {      
1433             if(scalexstr[0])
1434                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1435         }
1436     }
1437     if(scaleystr[0]) {
1438         if(oldheight==0) p.scaley = 1.0;
1439         else {
1440             if(scaleystr[0])
1441                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1442         }
1443     }
1444    
1445     /* rotation */
1446     if(rotatestr[0]) {
1447         if(isRelative(rotatestr)) {
1448             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1449         } else {
1450             p.rotate = parseFloat(rotatestr);
1451         }
1452     }
1453
1454     /* shearing */
1455     if(shearstr[0]) {
1456         if(isRelative(shearstr)) {
1457             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1458         } else {
1459             p.shear = parseFloat(shearstr);
1460         }
1461     }
1462
1463     if(pivotstr[0]) {
1464         if(isPoint(pivotstr)) 
1465             p.pivot = parsePoint(pivotstr);
1466         else 
1467             p.pivot = getPoint(oldbbox, pivotstr);
1468     }
1469     if(pinstr[0]) {
1470         if(isPoint(pinstr))
1471             p.pin = parsePoint(pinstr);
1472         else
1473             p.pin = getPoint(oldbbox, pinstr);
1474     }
1475         
1476     /* color transform */
1477
1478     if(rstr[0] || luminancestr[0]) {
1479         MULADD r;
1480         if(rstr[0])
1481             r = parseMulAdd(rstr);
1482         else {
1483             r.add = p.cxform.r0;
1484             r.mul = p.cxform.r1;
1485         }
1486         r = mergeMulAdd(r, luminance);
1487         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1488     }
1489     if(gstr[0] || luminancestr[0]) {
1490         MULADD g;
1491         if(gstr[0])
1492             g = parseMulAdd(gstr);
1493         else {
1494             g.add = p.cxform.g0;
1495             g.mul = p.cxform.g1;
1496         }
1497         g = mergeMulAdd(g, luminance);
1498         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1499     }
1500     if(bstr[0] || luminancestr[0]) {
1501         MULADD b;
1502         if(bstr[0])
1503             b = parseMulAdd(bstr);
1504         else {
1505             b.add = p.cxform.b0;
1506             b.mul = p.cxform.b1;
1507         }
1508         b = mergeMulAdd(b, luminance);
1509         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1510     }
1511     if(astr[0]) {
1512         MULADD a = parseMulAdd(astr);
1513         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1514     }
1515
1516     if(type == 0)
1517         s_put(instance, character, p);
1518     if(type == 1)
1519         s_change(instance, p);
1520     if(type == 2)
1521         s_qchange(instance, p);
1522     if(type == 3)
1523         s_jump(instance, p);
1524     if(type == 4)
1525         s_startclip(instance, character, p);
1526     return 0;
1527 }
1528 static int c_put(map_t*args) 
1529 {
1530     c_placement(args, 0);
1531     return 0;
1532 }
1533 static int c_change(map_t*args) 
1534 {
1535     c_placement(args, 1);
1536     return 0;
1537 }
1538 static int c_qchange(map_t*args) 
1539 {
1540     c_placement(args, 2);
1541     return 0;
1542 }
1543 static int c_arcchange(map_t*args) 
1544 {
1545     c_placement(args, 2);
1546     return 0;
1547 }
1548 static int c_jump(map_t*args) 
1549 {
1550     c_placement(args, 3);
1551     return 0;
1552 }
1553 static int c_startclip(map_t*args) 
1554 {
1555     c_placement(args, 4);
1556     return 0;
1557 }
1558 static int c_del(map_t*args) 
1559 {
1560     char*instance = lu(args, "name");
1561     s_delinstance(instance);
1562     return 0;
1563 }
1564 static int c_end(map_t*args) 
1565 {
1566     s_end();
1567     return 0;
1568 }
1569 static int c_sprite(map_t*args) 
1570 {
1571     char* name = lu(args, "name");
1572     s_sprite(name);
1573     return 0;
1574 }
1575 static int c_frame(map_t*args) 
1576 {
1577     char*framestr = lu(args, "n");
1578     int frame;
1579     if(isRelative(framestr)) {
1580         frame = s_getframe();
1581         if(getSign(framestr)<0)
1582             syntaxerror("relative frame expressions must be positive");
1583         frame += parseInt(getOffset(framestr));
1584     }
1585     else {
1586         frame = parseInt(framestr);
1587         if(s_getframe() >= frame
1588                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1589             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1590     }
1591     s_frame(frame);
1592     return 0;
1593 }
1594 static int c_primitive(map_t*args) 
1595 {
1596     char*name = lu(args, "name");
1597     char*command = lu(args, "commandname");
1598     int width=0, height=0, r=0;
1599     int linewidth = parseTwip(lu(args, "line"));
1600     char*colorstr = lu(args, "color");
1601     RGBA color = parseColor(colorstr);
1602     char*fillstr = lu(args, "fill");
1603     int dofill = 1;
1604     int type=0;
1605     char* font;
1606     char* text;
1607     RGBA fill;
1608     if(!strcmp(command, "circle"))
1609         type = 1;
1610     else if(!strcmp(command, "textshape"))
1611         type = 2;
1612    
1613     if(type==0) {
1614         width = parseTwip(lu(args, "width"));
1615         height = parseTwip(lu(args, "height"));
1616     } else if (type==1) {
1617         r = parseTwip(lu(args, "r"));
1618     } else if (type==2) {
1619         text = lu(args, "text");
1620         font = lu(args, "font");
1621     }
1622
1623     if(!strcmp(fillstr, "fill"))
1624         fillstr = colorstr;
1625     if(!strcmp(fillstr, "none"))
1626         dofill = 0;
1627     if(width<0 || height<0 || linewidth<0 || r<0)
1628         syntaxerror("values width, height, line, r must be positive");
1629     if(!dofill || isColor(fillstr)) {
1630         if(dofill) 
1631             fill = parseColor(fillstr);
1632     } else {
1633         /* FIXME - texture fill */
1634         fill.r = fill.g = 0;
1635         fill.b = fill.a = 255;
1636         warning("texture fill not supported yet. Filling with black.");
1637     }
1638     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1639     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1640     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1641     return 0;
1642 }
1643
1644 static int c_shape(map_t*args) 
1645 {
1646     char*name = lu(args, "name");
1647     char*filename = lu(args, "filename");
1648     s_shape(name, filename);
1649     return 0;
1650 }
1651
1652 static int c_font(map_t*args) 
1653 {
1654     char*name = lu(args, "name");
1655     char*filename = lu(args, "filename");
1656     s_font(name, filename);
1657     return 0;
1658 }
1659
1660 static int c_sound(map_t*args) 
1661 {
1662     char*name = lu(args, "name");
1663     char*filename = lu(args, "filename");
1664     s_sound(name, filename);
1665     return 0;
1666 }
1667
1668 static int c_text(map_t*args) 
1669 {
1670     char*name = lu(args, "name");
1671     char*text = lu(args, "text");
1672     char*font = lu(args, "font");
1673     float size = parsePercent(lu(args, "size"));
1674     RGBA color = parseColor(lu(args, "color"));
1675     s_text(name, font, text, (int)(size*100), color);
1676     return 0;
1677 }
1678
1679 static int c_soundtrack(map_t*args) 
1680 {
1681     return 0;
1682 }
1683
1684 int fakechar(map_t*args)
1685 {
1686     char*name = lu(args, "name");
1687     s_box(name, 0, 0, black, 20, black, 0);
1688     return 0;
1689 }
1690
1691 static int c_egon(map_t*args) {return fakechar(args);}
1692 static int c_button(map_t*args) {return fakechar(args);}
1693 static int c_edittext(map_t*args) {return fakechar(args);}
1694
1695 static int c_morphshape(map_t*args) {return fakechar(args);}
1696 static int c_image(map_t*args) {return fakechar(args);}
1697 static int c_movie(map_t*args) {return fakechar(args);}
1698
1699 static int c_buttonsounds(map_t*args) {return 0;}
1700 static int c_buttonput(map_t*args) {return 0;}
1701 static int c_texture(map_t*args) {return 0;}
1702 static int c_action(map_t*args) {return 0;}
1703
1704 static struct {
1705     char*command;
1706     command_func_t* func;
1707     char*arguments;
1708 } arguments[] =
1709 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1710  {"frame", c_frame, "n=<plus>1"},
1711
1712     // "import" type stuff
1713  {"shape", c_shape, "name filename"},
1714  {"morphshape", c_morphshape, "name start end"},
1715  {"jpeg", c_image, "name filename quality=80%"},
1716  {"png", c_image, "name filename"},
1717  {"movie", c_movie, "name filename"},
1718  {"sound", c_sound, "name filename"},
1719  {"font", c_font, "name filename"},
1720  {"soundtrack", c_soundtrack, "filename"},
1721
1722     // character generators
1723  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1724  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1725  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1726  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1727  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1728  {"text", c_text, "name text font size=100% color=white"},
1729  {"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"},
1730  
1731  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1732
1733     // control tags
1734  {"play", c_play, "sound loop=0 @nomultiple=0"},
1735  {"stop", c_stop, "sound"},
1736
1737     // object placement tags
1738  {"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="},
1739  {"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="},
1740  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1741  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1742  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1743  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1744  {"del", c_del, "name"},
1745     // virtual object placement
1746  {"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="},
1747  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1748  {"point", c_point, "name x=0 y=0"},
1749
1750     // commands which start a block
1751 //startclip (see above)
1752  {"sprite", c_sprite, "name"},
1753  {"action", c_action, ""},
1754
1755  {"end", c_end, ""}
1756 };
1757
1758
1759 static map_t parseArguments(char*command, char*pattern)
1760 {
1761     char*x;
1762     char*d,*e;
1763    
1764     string_t name[64];
1765     string_t value[64];
1766     int set[64];
1767     int isboolean[64];
1768     int pos;
1769     int len;
1770     int t;
1771     string_t t1,t2;
1772     map_t result;
1773     map_init(&result);
1774
1775     string_set(&t1, "commandname");
1776     string_set(&t2, command);
1777     map_put(&result, t1, t2);
1778
1779     if(!pattern || !*pattern)
1780         return result;
1781
1782     x = pattern;
1783
1784     pos = 0;
1785
1786     if(!strncmp("<i> ", x, 3)) {
1787         readToken();
1788         if(type == COMMAND || type == LABEL) {
1789             pushBack();
1790             syntaxerror("character name expected");
1791         }
1792         name[pos].str = "instance";
1793         name[pos].len = 8;
1794         value[pos].str = text;
1795         value[pos].len = strlen(text);
1796         set[pos] = 1;
1797         pos++;
1798
1799         if(type == ASSIGNMENT)
1800             readToken();
1801
1802         name[pos].str = "character";
1803         name[pos].len = 9;
1804         value[pos].str = text;
1805         value[pos].len = strlen(text);
1806         set[pos] = 1;
1807         pos++;
1808
1809         x+=4;
1810     }
1811
1812     while(*x) {
1813         isboolean[pos] = (x[0] =='@');
1814         if(isboolean[pos])
1815             x++;
1816
1817         d = strchr(x, ' ');
1818         e = strchr(x, '=');
1819         if(!d)
1820             d=&x[strlen(x)];
1821         set[pos] = 0;
1822
1823         if(!e || d<e) { 
1824             // no default
1825             name[pos].str = x;
1826             name[pos].len = d-x;
1827             value[pos].str = 0;
1828             value[pos].len = 0;
1829         } else {
1830             name[pos].str = x;
1831             name[pos].len = e-x;
1832             value[pos].str = e+1;
1833             value[pos].len = d-e-1;
1834         }
1835         pos++;
1836         if(!*d) break;
1837         x=d+1;
1838     }
1839     len = pos;
1840
1841 /*    for(t=0;t<len;t++) {
1842         printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1843                 isboolean[t]?"(boolean)":"");
1844     }*/
1845
1846     while(1) {
1847         readToken();
1848         if(type == LABEL || type == COMMAND) {
1849             pushBack();
1850             break;
1851         }
1852
1853         // first, search for boolean arguments
1854         for(pos=0;pos<len;pos++)
1855         {
1856             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1857                 set[pos] = 1;
1858                 if(type == ASSIGNMENT)
1859                     readToken();
1860                 value[pos].str = text;
1861                 value[pos].len = strlen(text);
1862                 /*printf("setting boolean parameter %s (to %s)\n",
1863                         strndup(name[pos], namelen[pos]),
1864                         strndup(value[pos], valuelen[pos]));*/
1865                 break;
1866             }
1867         }
1868
1869         // second, search for normal arguments
1870         if(pos==len)
1871         for(pos=0;pos<len;pos++)
1872         {
1873             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1874                (type != ASSIGNMENT && !set[pos])) {
1875                 if(set[pos]) {
1876                     syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1877                 }
1878                 if(type == ASSIGNMENT)
1879                     readToken();
1880                 set[pos] = 1;
1881                 value[pos].str = text;
1882                 value[pos].len = strlen(text);
1883 #if 0//def DEBUG
1884                 printf("setting parameter %s (to %s)\n",
1885                         strndup(name[pos].str, name[pos].len),
1886                         strndup(value[pos].str, value[pos].len));
1887 #endif
1888                 break;
1889             }
1890         }
1891         if(pos==len) {
1892             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1893         }
1894     }
1895 #if 0//def DEBUG
1896     for(t=0;t<len;t++) {
1897         printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1898     }
1899 #endif
1900     for(t=0;t<len;t++) {
1901         if(value[t].str && value[t].str[0] == '*') {
1902             //relative default- take value from some other parameter
1903             int s;
1904             for(s=0;s<len;s++) {
1905                 if(value[s].len == value[t].len-1 &&
1906                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1907                     value[t].str = value[s].str;
1908             }
1909         }
1910         if(value[t].str == 0) {
1911             pushBack();
1912             syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1913         }
1914     }
1915
1916     /* ok, now construct the dictionary from the parameters */
1917
1918     for(t=0;t<len;t++) 
1919     {
1920         map_put(&result, name[t], value[t]);
1921     }
1922     return result;
1923 }
1924 static void parseArgumentsForCommand(char*command)
1925 {
1926     int t;
1927     map_t args;
1928     int nr = -1;
1929     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1930         if(!strcmp(arguments[t].command, command)) {
1931             args = parseArguments(command, arguments[t].arguments);
1932             nr = t;
1933         }
1934     }
1935     if(nr<0)
1936         syntaxerror("command %s not known", command);
1937
1938 #ifdef DEBUG
1939     printf(".%s\n", command);fflush(stdout);
1940     map_dump(&args, stdout, "\t");fflush(stdout);
1941 #endif
1942
1943     (*arguments[nr].func)(&args);
1944
1945     if(!strcmp(command, "button") ||
1946        !strcmp(command, "action")) {
1947         while(1) {
1948             readToken();
1949             if(type == COMMAND) {
1950                 if(!strcmp(text, "end"))
1951                     break;
1952                 else {
1953                     pushBack();
1954                     break;
1955                 }
1956             }
1957         }
1958     }
1959
1960     map_clear(&args);
1961     return;
1962 }
1963
1964 int main (int argc,char ** argv)
1965
1966     int t;
1967     processargs(argc, argv);
1968     initLog(0,-1,0,0,-1,verbose);
1969
1970     if(!filename) {
1971         args_callback_usage(argv[0]);
1972         exit(1);
1973     }
1974     file = generateTokens(filename);
1975     if(!file) {
1976         printf("parser returned error.\n");
1977         return 1;
1978     }
1979     pos=0;
1980
1981     t=0;
1982
1983     while(!noMoreTokens()) {
1984         readToken();
1985         if(type != COMMAND)
1986             syntaxerror("command expected");
1987         parseArgumentsForCommand(text);
1988     }
1989
1990     s_close();
1991     freeTokens(file);
1992
1993     return 0;
1994 }
1995