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