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