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