fixed "identifier may not start with a digit" syntax error.
[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;
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     l = (int)dictionary_lookup(&points, name);
1225     if(l==0) {
1226         syntaxerror("Couldn't find point \"%s\".", name);
1227     }
1228     l--;
1229     return *(SPOINT*)&mpoints.buffer[l];
1230 }
1231 static int c_point(map_t*args) 
1232 {
1233     char*name = lu(args, "name");
1234     int pos;
1235     string_t s1;
1236     SPOINT p;
1237     if(!points_initialized) {
1238         dictionary_init(&points);
1239         mem_init(&mpoints);
1240         points_initialized = 1;
1241     }
1242     p.x = parseTwip(lu(args, "x"));
1243     p.y = parseTwip(lu(args, "y"));
1244     pos = mem_put(&mpoints, &p, sizeof(p));
1245     string_set(&s1, name);
1246     pos++;
1247     dictionary_put(&points, s1, (void*)pos);
1248     return 0;
1249 }
1250 static int c_placement(map_t*args, int type)
1251 {
1252     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1253     char*character = 0;
1254
1255     char* luminancestr = lu(args, "luminance");
1256     char* scalestr = lu(args, "scale");
1257     char* scalexstr = lu(args, "scalex");
1258     char* scaleystr = lu(args, "scaley");
1259     char* rotatestr = lu(args, "rotate");
1260     char* shearstr = lu(args, "shear");
1261     char* xstr="", *pivotstr="";
1262     char* ystr="", *anglestr="";
1263     char*above = lu(args, "above"); /*FIXME*/
1264     char*below = lu(args, "below");
1265     char* rstr = lu(args, "red");
1266     char* gstr = lu(args, "green");
1267     char* bstr = lu(args, "blue");
1268     char* astr = lu(args, "alpha");
1269     char* pinstr = lu(args, "pin");
1270     MULADD r,g,b,a;
1271     float oldwidth;
1272     float oldheight;
1273     SRECT oldbbox;
1274     MULADD luminance;
1275     parameters_t p;
1276
1277     if(type==5) {
1278         pivotstr = lu(args, "pivot");
1279         anglestr = lu(args, "angle");
1280     } else {
1281         xstr = lu(args, "x");
1282         ystr = lu(args, "y");
1283     }
1284     if(luminancestr[0])
1285         luminance = parseMulAdd(luminancestr);
1286     else {
1287         luminance.add = 0;
1288         luminance.mul = 256;
1289     }
1290
1291     if(scalestr[0]) {
1292         if(scalexstr[0]||scaleystr[0])
1293             syntaxerror("scalex/scaley and scale cannot both be set");
1294         scalexstr = scaleystr = scalestr;
1295     }
1296     
1297     if(type == 0 || type == 4)  {
1298         // put or startclip
1299         character = lu(args, "character");
1300         parameters_clear(&p);
1301     } else {
1302         p = s_getParameters(instance);
1303     }
1304
1305     /* x,y position */
1306     if(xstr[0]) {
1307         if(isRelative(xstr)) {
1308             if(type == 0 || type == 4)
1309                 syntaxerror("relative x values not allowed for initial put or startclip");
1310             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1311         } else {
1312             p.x = parseTwip(xstr);
1313         }
1314     }
1315     if(ystr[0]) {
1316         if(isRelative(ystr)) {
1317             if(type == 0 || type == 4)
1318                 syntaxerror("relative y values not allowed for initial put or startclip");
1319             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1320         } else {
1321             p.y = parseTwip(ystr);
1322         }
1323     }
1324
1325     /* scale, scalex, scaley */
1326     if(character) {
1327         oldbbox = s_getCharBBox(character);
1328     } else {
1329         oldbbox = s_getInstanceBBox(instance);
1330     }
1331     oldwidth = oldbbox.xmax - oldbbox.xmin;
1332     oldheight = oldbbox.ymax - oldbbox.ymin;
1333     if(scalexstr[0]) {
1334         if(oldwidth==0) p.scalex = 1.0;
1335         else {      
1336             if(scalexstr[0])
1337                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1338         }
1339     }
1340     if(scaleystr[0]) {
1341         if(oldheight==0) p.scaley = 1.0;
1342         else {
1343             if(scaleystr[0])
1344                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1345         }
1346     }
1347    
1348     /* rotation */
1349     if(rotatestr[0]) {
1350         if(isRelative(rotatestr)) {
1351             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1352         } else {
1353             p.rotate = parseFloat(rotatestr);
1354         }
1355     }
1356
1357     /* shearing */
1358     if(shearstr[0]) {
1359         if(isRelative(shearstr)) {
1360             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1361         } else {
1362             p.shear = parseFloat(shearstr);
1363         }
1364     }
1365
1366     if(pivotstr[0]) {
1367         if(isPoint(pivotstr)) 
1368             p.pivot = parsePoint(pivotstr);
1369         else 
1370             p.pivot = getPoint(oldbbox, pivotstr);
1371     }
1372     if(pinstr[0]) {
1373         if(isPoint(pinstr))
1374             p.pin = parsePoint(pinstr);
1375         else
1376             p.pin = getPoint(oldbbox, pinstr);
1377     }
1378         
1379     /* color transform */
1380
1381     if(rstr[0] || luminancestr[0]) {
1382         MULADD r;
1383         if(rstr[0])
1384             r = parseMulAdd(rstr);
1385         else {
1386             r.add = p.cxform.r0;
1387             r.mul = p.cxform.r1;
1388         }
1389         r = mergeMulAdd(r, luminance);
1390         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1391     }
1392     if(gstr[0] || luminancestr[0]) {
1393         MULADD g;
1394         if(gstr[0])
1395             g = parseMulAdd(gstr);
1396         else {
1397             g.add = p.cxform.g0;
1398             g.mul = p.cxform.g1;
1399         }
1400         g = mergeMulAdd(g, luminance);
1401         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1402     }
1403     if(bstr[0] || luminancestr[0]) {
1404         MULADD b;
1405         if(bstr[0])
1406             b = parseMulAdd(bstr);
1407         else {
1408             b.add = p.cxform.b0;
1409             b.mul = p.cxform.b1;
1410         }
1411         b = mergeMulAdd(b, luminance);
1412         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1413     }
1414     if(astr[0]) {
1415         MULADD a = parseMulAdd(astr);
1416         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1417     }
1418
1419     if(type == 0)
1420         s_put(instance, character, p);
1421     if(type == 1)
1422         s_change(instance, p);
1423     if(type == 2)
1424         s_qchange(instance, p);
1425     if(type == 3)
1426         s_jump(instance, p);
1427     if(type == 4)
1428         s_startclip(instance, character, p);
1429     return 0;
1430 }
1431 static int c_put(map_t*args) 
1432 {
1433     c_placement(args, 0);
1434     return 0;
1435 }
1436 static int c_change(map_t*args) 
1437 {
1438     c_placement(args, 1);
1439     return 0;
1440 }
1441 static int c_qchange(map_t*args) 
1442 {
1443     c_placement(args, 2);
1444     return 0;
1445 }
1446 static int c_arcchange(map_t*args) 
1447 {
1448     c_placement(args, 2);
1449     return 0;
1450 }
1451 static int c_jump(map_t*args) 
1452 {
1453     c_placement(args, 3);
1454     return 0;
1455 }
1456 static int c_startclip(map_t*args) 
1457 {
1458     c_placement(args, 4);
1459     return 0;
1460 }
1461 static int c_del(map_t*args) 
1462 {
1463     char*instance = lu(args, "name");
1464     s_delinstance(instance);
1465     return 0;
1466 }
1467 static int c_end(map_t*args) 
1468 {
1469     s_end();
1470     return 0;
1471 }
1472 static int c_sprite(map_t*args) 
1473 {
1474     char* name = lu(args, "name");
1475     s_sprite(name);
1476     return 0;
1477 }
1478 static int c_frame(map_t*args) 
1479 {
1480     char*framestr = lu(args, "n");
1481     int frame;
1482     if(isRelative(framestr)) {
1483         frame = s_getframe();
1484         if(getSign(framestr)<0)
1485             syntaxerror("relative frame expressions must be positive");
1486         frame += parseInt(getOffset(framestr));
1487     }
1488     else {
1489         frame = parseInt(framestr);
1490         if(s_getframe() >= frame
1491                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1492             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1493     }
1494     s_frame(frame);
1495     return 0;
1496 }
1497 static int c_primitive(map_t*args) 
1498 {
1499     char*name = lu(args, "name");
1500     char*command = lu(args, "commandname");
1501     int width=0, height=0, r=0;
1502     int linewidth = parseTwip(lu(args, "line"));
1503     char*colorstr = lu(args, "color");
1504     RGBA color = parseColor(colorstr);
1505     char*fillstr = lu(args, "fill");
1506     int dofill = 1;
1507     int type=0;
1508     char* font;
1509     char* text;
1510     RGBA fill;
1511     if(!strcmp(command, "circle"))
1512         type = 1;
1513     else if(!strcmp(command, "textshape"))
1514         type = 2;
1515    
1516     if(type==0) {
1517         width = parseTwip(lu(args, "width"));
1518         height = parseTwip(lu(args, "height"));
1519     } else if (type==1) {
1520         r = parseTwip(lu(args, "r"));
1521     } else if (type==2) {
1522         text = lu(args, "text");
1523         font = lu(args, "font");
1524     }
1525
1526     if(!strcmp(fillstr, "fill"))
1527         fillstr = colorstr;
1528     if(!strcmp(fillstr, "none"))
1529         dofill = 0;
1530     if(width<0 || height<0 || linewidth<0 || r<0)
1531         syntaxerror("values width, height, line, r must be positive");
1532     if(!dofill || isColor(fillstr)) {
1533         if(dofill) 
1534             fill = parseColor(fillstr);
1535     } else {
1536         /* FIXME - texture fill */
1537         fill.r = fill.g = 0;
1538         fill.b = fill.a = 255;
1539         warning("texture fill not supported yet. Filling with black.");
1540     }
1541     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1542     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1543     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1544     return 0;
1545 }
1546
1547 static int c_shape(map_t*args) 
1548 {
1549     char*name = lu(args, "name");
1550     char*filename = lu(args, "filename");
1551     s_shape(name, filename);
1552     return 0;
1553 }
1554
1555 static int c_font(map_t*args) 
1556 {
1557     char*name = lu(args, "name");
1558     char*filename = lu(args, "filename");
1559     s_font(name, filename);
1560     return 0;
1561 }
1562
1563 static int c_text(map_t*args) 
1564 {
1565     char*name = lu(args, "name");
1566     char*text = lu(args, "text");
1567     char*font = lu(args, "font");
1568     float size = parsePercent(lu(args, "size"));
1569     RGBA color = parseColor(lu(args, "color"));
1570     s_text(name, font, text, (int)(size*100), color);
1571     return 0;
1572 }
1573
1574 int fakechar(map_t*args)
1575 {
1576     char*name = lu(args, "name");
1577     s_box(name, 0, 0, black, 20, black, 0);
1578     return 0;
1579 }
1580 static int c_circle(map_t*args) {return fakechar(args);}
1581
1582 static int c_egon(map_t*args) {return fakechar(args);}
1583 static int c_button(map_t*args) {return fakechar(args);}
1584 static int c_edittext(map_t*args) {return fakechar(args);}
1585
1586 static int c_morphshape(map_t*args) {return fakechar(args);}
1587 static int c_image(map_t*args) {return fakechar(args);}
1588 static int c_movie(map_t*args) {return fakechar(args);}
1589 static int c_sound(map_t*args) {return fakechar(args);}
1590
1591 static int c_play(map_t*args) {return 0;}
1592 static int c_stop(map_t*args) {return 0;}
1593
1594 static int c_soundtrack(map_t*args) {return 0;}
1595 static int c_buttonsounds(map_t*args) {return 0;}
1596 static int c_buttonput(map_t*args) {return 0;}
1597 static int c_texture(map_t*args) {return 0;}
1598 static int c_action(map_t*args) {return 0;}
1599
1600 static struct {
1601     char*command;
1602     command_func_t* func;
1603     char*arguments;
1604 } arguments[] =
1605 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1606  {"frame", c_frame, "n=<plus>1"},
1607
1608     // "import" type stuff
1609  {"shape", c_shape, "name filename"},
1610  {"morphshape", c_morphshape, "name start end"},
1611  {"jpeg", c_image, "name filename quality=80%"},
1612  {"png", c_image, "name filename"},
1613  {"movie", c_movie, "name filename"},
1614  {"sound", c_sound, "name filename"},
1615  {"font", c_font, "name filename"},
1616  {"soundtrack", c_soundtrack, "filename"},
1617
1618     // character generators
1619  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1620  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1621  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1622  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1623  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1624  {"text", c_text, "name text font size=100% color=white"},
1625  {"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"},
1626  
1627  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1628
1629     // control tags
1630  {"play", c_play, "sound loop=0 @nomultiple=0"},
1631  {"stop", c_stop, "sound"},
1632
1633     // object placement tags
1634  {"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="},
1635  {"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="},
1636  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1637  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1638  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1639  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1640  {"del", c_del, "name"},
1641     // virtual object placement
1642  {"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="},
1643  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1644  {"point", c_point, "name x=0 y=0"},
1645
1646     // commands which start a block
1647 //startclip (see above)
1648  {"sprite", c_sprite, "name"},
1649  {"action", c_action, ""},
1650
1651  {"end", c_end, ""}
1652 };
1653
1654
1655 static map_t parseArguments(char*command, char*pattern)
1656 {
1657     char*x;
1658     char*d,*e;
1659    
1660     string_t name[64];
1661     string_t value[64];
1662     int set[64];
1663     int isboolean[64];
1664     int pos;
1665     int len;
1666     int t;
1667     string_t t1,t2;
1668     map_t result;
1669     map_init(&result);
1670
1671     string_set(&t1, "commandname");
1672     string_set(&t2, command);
1673     map_put(&result, t1, t2);
1674
1675     if(!pattern || !*pattern)
1676         return result;
1677
1678     x = pattern;
1679
1680     pos = 0;
1681
1682     if(!strncmp("<i> ", x, 3)) {
1683         readToken();
1684         if(type == COMMAND || type == LABEL) {
1685             pushBack();
1686             syntaxerror("character name expected");
1687         }
1688         name[pos].str = "instance";
1689         name[pos].len = 8;
1690         value[pos].str = text;
1691         value[pos].len = strlen(text);
1692         set[pos] = 1;
1693         pos++;
1694
1695         if(type == ASSIGNMENT)
1696             readToken();
1697
1698         name[pos].str = "character";
1699         name[pos].len = 9;
1700         value[pos].str = text;
1701         value[pos].len = strlen(text);
1702         set[pos] = 1;
1703         pos++;
1704
1705         x+=4;
1706     }
1707
1708     while(*x) {
1709         isboolean[pos] = (x[0] =='@');
1710         if(isboolean[pos])
1711             x++;
1712
1713         d = strchr(x, ' ');
1714         e = strchr(x, '=');
1715         if(!d)
1716             d=&x[strlen(x)];
1717         set[pos] = 0;
1718
1719         if(!e || d<e) { 
1720             // no default
1721             name[pos].str = x;
1722             name[pos].len = d-x;
1723             value[pos].str = 0;
1724             value[pos].len = 0;
1725         } else {
1726             name[pos].str = x;
1727             name[pos].len = e-x;
1728             value[pos].str = e+1;
1729             value[pos].len = d-e-1;
1730         }
1731         pos++;
1732         if(!*d) break;
1733         x=d+1;
1734     }
1735     len = pos;
1736
1737 /*    for(t=0;t<len;t++) {
1738         printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1739                 isboolean[t]?"(boolean)":"");
1740     }*/
1741
1742     while(1) {
1743         readToken();
1744         if(type == LABEL || type == COMMAND) {
1745             pushBack();
1746             break;
1747         }
1748
1749         // first, search for boolean arguments
1750         for(pos=0;pos<len;pos++)
1751         {
1752             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1753                 set[pos] = 1;
1754                 if(type == ASSIGNMENT)
1755                     readToken();
1756                 value[pos].str = text;
1757                 value[pos].len = strlen(text);
1758                 /*printf("setting boolean parameter %s (to %s)\n",
1759                         strndup(name[pos], namelen[pos]),
1760                         strndup(value[pos], valuelen[pos]));*/
1761                 break;
1762             }
1763         }
1764
1765         // second, search for normal arguments
1766         if(pos==len)
1767         for(pos=0;pos<len;pos++)
1768         {
1769             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1770                (type != ASSIGNMENT && !set[pos])) {
1771                 if(set[pos]) {
1772                     syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1773                 }
1774                 if(type == ASSIGNMENT)
1775                     readToken();
1776                 set[pos] = 1;
1777                 value[pos].str = text;
1778                 value[pos].len = strlen(text);
1779 #if 0//def DEBUG
1780                 printf("setting parameter %s (to %s)\n",
1781                         strndup(name[pos].str, name[pos].len),
1782                         strndup(value[pos].str, value[pos].len));
1783 #endif
1784                 break;
1785             }
1786         }
1787         if(pos==len) {
1788             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1789         }
1790     }
1791 #if 0//def DEBUG
1792     for(t=0;t<len;t++) {
1793         printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1794     }
1795 #endif
1796     for(t=0;t<len;t++) {
1797         if(value[t].str && value[t].str[0] == '*') {
1798             //relative default- take value from some other parameter
1799             int s;
1800             for(s=0;s<len;s++) {
1801                 if(value[s].len == value[t].len-1 &&
1802                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1803                     value[t].str = value[s].str;
1804             }
1805         }
1806         if(value[t].str == 0) {
1807             pushBack();
1808             syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1809         }
1810     }
1811
1812     /* ok, now construct the dictionary from the parameters */
1813
1814     for(t=0;t<len;t++) 
1815     {
1816         map_put(&result, name[t], value[t]);
1817     }
1818     return result;
1819 }
1820 static void parseArgumentsForCommand(char*command)
1821 {
1822     int t;
1823     map_t args;
1824     int nr = -1;
1825     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1826         if(!strcmp(arguments[t].command, command)) {
1827             args = parseArguments(command, arguments[t].arguments);
1828             nr = t;
1829         }
1830     }
1831     if(nr<0)
1832         syntaxerror("command %s not known", command);
1833
1834 #ifdef DEBUG
1835     printf(".%s\n", command);fflush(stdout);
1836     map_dump(&args, stdout, "\t");fflush(stdout);
1837 #endif
1838
1839     (*arguments[nr].func)(&args);
1840
1841     if(!strcmp(command, "button") ||
1842        !strcmp(command, "action")) {
1843         while(1) {
1844             readToken();
1845             if(type == COMMAND) {
1846                 if(!strcmp(text, "end"))
1847                     break;
1848                 else {
1849                     pushBack();
1850                     break;
1851                 }
1852             }
1853         }
1854     }
1855
1856     map_clear(&args);
1857     return;
1858 }
1859
1860 int main (int argc,char ** argv)
1861
1862     int t;
1863     processargs(argc, argv);
1864     initLog(0,-1,0,0,-1,verbose);
1865
1866     if(!filename) {
1867         args_callback_usage(argv[0]);
1868         exit(1);
1869     }
1870     file = generateTokens(filename);
1871     if(!file) {
1872         printf("parser returned error.\n");
1873         return 1;
1874     }
1875     pos=0;
1876
1877     t=0;
1878
1879     while(!noMoreTokens()) {
1880         readToken();
1881         if(type != COMMAND)
1882             syntaxerror("command expected");
1883         parseArgumentsForCommand(text);
1884     }
1885
1886     s_close();
1887     freeTokens(file);
1888
1889     return 0;
1890 }
1891