win32 fixes
[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
1046     struct {unsigned char r,g,b;char*name;} colors[] =
1047     {{255,250,250,"snow"},{220,220,220,"gainsboro"},{250,240,230,"linen"},{255,228,196,"bisque"},
1048     {255,228,181,"moccasin"},{255,248,220,"cornsilk"},{255,255,240,"ivory"},{255,245,238,"seashell"},
1049     {240,255,240,"honeydew"},{240,255,255,"azure"},{230,230,250,"lavender"},{255,255,255,"white"},
1050     {0,0,0,"black"},{190,190,190,"gray"},{190,190,190,"grey"},{0,0,128,"navy"},{0,0,255,"blue"},
1051     {64,224,208,"turquoise"},{0,255,255,"cyan"},{127,255,212,"aquamarine"}, {0,255,0,"green"},
1052     {127,255,0,"chartreuse"},{240,230,140,"khaki"},{255,255,0,"yellow"},
1053     {255,215,0,"gold"},{218,165,32,"goldenrod"},{160,82,45,"sienna"},{205,133,63,"peru"},
1054     {222,184,135,"burlywood"},{245,245,220,"beige"},{245,222,179,"wheat"},{210,180,140,"tan"},
1055     {210,105,30,"chocolate"},{178,34,34,"firebrick"},{165,42,42,"brown"},{250,128,114,"salmon"},
1056     {255,165,0,"orange"},{255,127,80,"coral"},{255,99,71,"tomato"},{255,0,0,"red"},
1057     {255,192,203,"pink"},{176,48,96,"maroon"},{255,0,255,"magenta"},{238,130,238,"violet"},
1058     {221,160,221,"plum"},{218,112,214,"orchid"},{160,32,240,"purple"},{216,191,216,"thistle"}};
1059
1060     a=255;r=g=b=0;
1061
1062     if(str[0]=='#' && (l==7 || l==9)) {
1063         if(l == 7 && !(sscanf(str, "#%02x%02x%02x", &r, &g, &b)))
1064             return 0;
1065         if(l == 9 && !(sscanf(str, "#%02x%02x%02x%02x", &r, &g, &b, &a)))
1066             return 0;
1067         color->r = r; color->g = g; color->b = b; color->a = a;
1068         return 1;
1069     }
1070     for(t=0;t<sizeof(colors)/sizeof(colors[0]);t++)
1071         if(!strcmp(str, colors[t].name)) {
1072             r = colors[t].r;
1073             g = colors[t].g;
1074             b = colors[t].b;
1075             a = 255;
1076             color->r = r; color->g = g; color->b = b; color->a = a;
1077             return 1;
1078         }
1079     return 0;
1080
1081 }
1082 RGBA parseColor(char*str)
1083 {
1084     RGBA c;
1085     if(!parseColor2(str, &c))
1086         syntaxerror("Expression '%s' is not a color", str);
1087     return c;
1088 }
1089
1090 typedef struct _muladd {
1091     S16 mul;
1092     S16 add;
1093 } MULADD;
1094
1095 MULADD parseMulAdd(char*str)
1096 {
1097     float add, mul;
1098     char* str2 = (char*)malloc(strlen(str)+5);
1099     int i;
1100     MULADD m;
1101     strcpy(str2, str);
1102     strcat(str2, " 0");
1103     add = 0;
1104     mul = 1.0;
1105     if(sscanf(str2, "%f%f %d", &mul, &add, &i)==2+1) { add/=256.0; i=1;}
1106     else if(sscanf(str2, "%f%%%f %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=256.0; i=2;}
1107     else if(sscanf(str2, "%f%f%% %d", &mul, &add, &i)==2+1) { add/=100.0; i=3;}
1108     else if(sscanf(str2, "%f%%%f%% %d", &mul, &add, &i)==2+1) { mul/=100.0; add/=100.0; i=4;}
1109     else if(sscanf(str2, "+%f%% %d", &add, &i)==1+1) { mul=1.0;add/=100.0; i=5;}
1110     else if(sscanf(str2, "+%f %d", &add, &i)==1+1) { mul=1.0;add/=256.0; i=6;}
1111     else if(sscanf(str2, "-%f%% %d", &add, &i)==1+1) { mul=1.0;add/=-100.0; i=7;}
1112     else if(sscanf(str2, "-%f %d", &add, &i)==1+1) { mul=1.0;add/=-256.0; i=8;}
1113     else if(sscanf(str2, "%f%% %d", &mul, &i)==1+1) { mul/=100.0;add=0; i=9;}
1114     else if(sscanf(str2, "%f %d", &mul, &i)==1+1) { add=0; i=10;}
1115     else {
1116         syntaxerror("'%s' is not a valid color transform expression", str);
1117     }
1118     m.add = (int)(add*256);
1119     m.mul = (int)(mul*256);
1120     free(str2);
1121     return m;
1122 }
1123
1124 MULADD mergeMulAdd(MULADD m1, MULADD m2)
1125 {
1126     int a = (int)((double)m2.add+((double)m1.add*(double)m2.mul)/256.0);
1127     double m = ((double)m1.mul*(double)m2.mul)/256.0;
1128     MULADD r;
1129     if(a<-32768) a=-32768;
1130     if(a>32767) a=32767;
1131     if(m<-32768) m=-32768;
1132     if(m>32767) m=32767;
1133     r.add = a;
1134     r.mul = (int)m;
1135     return r;
1136 }
1137
1138 float parsePercent(char*str)
1139 {
1140     int l = strlen(str);
1141     if(!l)
1142         return 1.0;
1143     if(str[l-1]=='%') {
1144         return atoi(str)/100.0;
1145     }
1146     syntaxerror("Expression '%s' is not a percentage", str);
1147     return 0;
1148 }
1149 int isPercent(char*str)
1150 {
1151     return str[strlen(str)-1]=='%';
1152 }
1153 int parseNewSize(char*str, int size)
1154 {
1155     if(isPercent(str))
1156         return parsePercent(str)*size;
1157     else
1158         return (int)(atof(str)*20);
1159 }
1160
1161 int isColor(char*str)
1162 {
1163     RGBA c;
1164     return parseColor2(str, &c);
1165 }
1166
1167 static char* lu(map_t* args, char*name)
1168 {
1169     char* value = map_lookup(args, name);
1170     if(!value) {
1171         syntaxerror("internal error 2: value %s should be set", name);
1172     }
1173     return value;
1174 }
1175
1176 static int c_swf(map_t*args) 
1177 {
1178     char* name = lu(args, "name");
1179     char* compressstr = lu(args, "compress");
1180     SRECT bbox = parseBox(lu(args, "bbox"));
1181     int version = parseInt(lu(args, "version"));
1182     int fps = (int)(parseFloat(lu(args, "fps"))*256);
1183     int compress = 0;
1184     if(!strcmp(name, "!default!") || override_outputname)
1185         name = outputname;
1186     
1187     if(!strcmp(compressstr, "default"))
1188         compress = version==6;
1189     else if(!strcmp(compressstr, "yes") || !strcmp(compressstr, "compress"))
1190         compress = 1;
1191     else if(!strcmp(compressstr, "no"))
1192         compress = 0;
1193     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
1194
1195     s_swf(name, bbox, version, fps, compress);
1196     return 0;
1197 }
1198 int isRelative(char*str)
1199 {
1200     return !strncmp(str, "<plus>", 6) ||
1201            !strncmp(str, "<minus>", 7);
1202 }
1203 char* getOffset(char*str)
1204 {
1205     if(!strncmp(str, "<plus>", 6))
1206         return str+6;
1207     if(!strncmp(str, "<minus>", 7))
1208         return str+7;
1209     syntaxerror("internal error (347)");
1210     return 0;
1211 }
1212 int getSign(char*str)
1213 {
1214     if(!strncmp(str, "<plus>", 6))
1215         return 1;
1216     if(!strncmp(str, "<minus>", 7))
1217         return -1;
1218     syntaxerror("internal error (348)");
1219     return 0;
1220 }
1221 static dictionary_t points;
1222 static mem_t mpoints;
1223 int points_initialized = 0;
1224
1225 SPOINT getPoint(SRECT r, char*name)
1226 {
1227     int l=0;
1228     if(!strcmp(name, "center")) {
1229         SPOINT p;
1230         p.x = (r.xmin + r.xmax)/2;
1231         p.y = (r.ymin + r.ymax)/2;
1232         return p;
1233     }
1234
1235     if(points_initialized)
1236         l = (int)dictionary_lookup(&points, name);
1237     if(l==0) {
1238         syntaxerror("Invalid point: \"%s\".", name);
1239     }
1240     l--;
1241     return *(SPOINT*)&mpoints.buffer[l];
1242 }
1243 static int c_point(map_t*args) 
1244 {
1245     char*name = lu(args, "name");
1246     int pos;
1247     string_t s1;
1248     SPOINT p;
1249     if(!points_initialized) {
1250         dictionary_init(&points);
1251         mem_init(&mpoints);
1252         points_initialized = 1;
1253     }
1254     p.x = parseTwip(lu(args, "x"));
1255     p.y = parseTwip(lu(args, "y"));
1256     pos = mem_put(&mpoints, &p, sizeof(p));
1257     string_set(&s1, name);
1258     pos++;
1259     dictionary_put(&points, s1, (void*)pos);
1260     return 0;
1261 }
1262 static int c_placement(map_t*args, int type)
1263 {
1264     char*instance = lu(args, (type==0||type==4)?"instance":"name");
1265     char*character = 0;
1266
1267     char* luminancestr = lu(args, "luminance");
1268     char* scalestr = lu(args, "scale");
1269     char* scalexstr = lu(args, "scalex");
1270     char* scaleystr = lu(args, "scaley");
1271     char* rotatestr = lu(args, "rotate");
1272     char* shearstr = lu(args, "shear");
1273     char* xstr="", *pivotstr="";
1274     char* ystr="", *anglestr="";
1275     char*above = lu(args, "above"); /*FIXME*/
1276     char*below = lu(args, "below");
1277     char* rstr = lu(args, "red");
1278     char* gstr = lu(args, "green");
1279     char* bstr = lu(args, "blue");
1280     char* astr = lu(args, "alpha");
1281     char* pinstr = lu(args, "pin");
1282     MULADD r,g,b,a;
1283     float oldwidth;
1284     float oldheight;
1285     SRECT oldbbox;
1286     MULADD luminance;
1287     parameters_t p;
1288
1289     if(type==5) {
1290         pivotstr = lu(args, "pivot");
1291         anglestr = lu(args, "angle");
1292     } else {
1293         xstr = lu(args, "x");
1294         ystr = lu(args, "y");
1295     }
1296     if(luminancestr[0])
1297         luminance = parseMulAdd(luminancestr);
1298     else {
1299         luminance.add = 0;
1300         luminance.mul = 256;
1301     }
1302
1303     if(scalestr[0]) {
1304         if(scalexstr[0]||scaleystr[0])
1305             syntaxerror("scalex/scaley and scale cannot both be set");
1306         scalexstr = scaleystr = scalestr;
1307     }
1308     
1309     if(type == 0 || type == 4)  {
1310         // put or startclip
1311         character = lu(args, "character");
1312         parameters_clear(&p);
1313     } else {
1314         p = s_getParameters(instance);
1315     }
1316
1317     /* x,y position */
1318     if(xstr[0]) {
1319         if(isRelative(xstr)) {
1320             if(type == 0 || type == 4)
1321                 syntaxerror("relative x values not allowed for initial put or startclip");
1322             p.x += parseTwip(getOffset(xstr))*getSign(xstr);
1323         } else {
1324             p.x = parseTwip(xstr);
1325         }
1326     }
1327     if(ystr[0]) {
1328         if(isRelative(ystr)) {
1329             if(type == 0 || type == 4)
1330                 syntaxerror("relative y values not allowed for initial put or startclip");
1331             p.y += parseTwip(getOffset(ystr))*getSign(ystr);
1332         } else {
1333             p.y = parseTwip(ystr);
1334         }
1335     }
1336
1337     /* scale, scalex, scaley */
1338     if(character) {
1339         oldbbox = s_getCharBBox(character);
1340     } else {
1341         oldbbox = s_getInstanceBBox(instance);
1342     }
1343     oldwidth = oldbbox.xmax - oldbbox.xmin;
1344     oldheight = oldbbox.ymax - oldbbox.ymin;
1345     if(scalexstr[0]) {
1346         if(oldwidth==0) p.scalex = 1.0;
1347         else {      
1348             if(scalexstr[0])
1349                 p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
1350         }
1351     }
1352     if(scaleystr[0]) {
1353         if(oldheight==0) p.scaley = 1.0;
1354         else {
1355             if(scaleystr[0])
1356                 p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
1357         }
1358     }
1359    
1360     /* rotation */
1361     if(rotatestr[0]) {
1362         if(isRelative(rotatestr)) {
1363             p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
1364         } else {
1365             p.rotate = parseFloat(rotatestr);
1366         }
1367     }
1368
1369     /* shearing */
1370     if(shearstr[0]) {
1371         if(isRelative(shearstr)) {
1372             p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
1373         } else {
1374             p.shear = parseFloat(shearstr);
1375         }
1376     }
1377
1378     if(pivotstr[0]) {
1379         if(isPoint(pivotstr)) 
1380             p.pivot = parsePoint(pivotstr);
1381         else 
1382             p.pivot = getPoint(oldbbox, pivotstr);
1383     }
1384     if(pinstr[0]) {
1385         if(isPoint(pinstr))
1386             p.pin = parsePoint(pinstr);
1387         else
1388             p.pin = getPoint(oldbbox, pinstr);
1389     }
1390         
1391     /* color transform */
1392
1393     if(rstr[0] || luminancestr[0]) {
1394         MULADD r;
1395         if(rstr[0])
1396             r = parseMulAdd(rstr);
1397         else {
1398             r.add = p.cxform.r0;
1399             r.mul = p.cxform.r1;
1400         }
1401         r = mergeMulAdd(r, luminance);
1402         p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
1403     }
1404     if(gstr[0] || luminancestr[0]) {
1405         MULADD g;
1406         if(gstr[0])
1407             g = parseMulAdd(gstr);
1408         else {
1409             g.add = p.cxform.g0;
1410             g.mul = p.cxform.g1;
1411         }
1412         g = mergeMulAdd(g, luminance);
1413         p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
1414     }
1415     if(bstr[0] || luminancestr[0]) {
1416         MULADD b;
1417         if(bstr[0])
1418             b = parseMulAdd(bstr);
1419         else {
1420             b.add = p.cxform.b0;
1421             b.mul = p.cxform.b1;
1422         }
1423         b = mergeMulAdd(b, luminance);
1424         p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
1425     }
1426     if(astr[0]) {
1427         MULADD a = parseMulAdd(astr);
1428         p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
1429     }
1430
1431     if(type == 0)
1432         s_put(instance, character, p);
1433     if(type == 1)
1434         s_change(instance, p);
1435     if(type == 2)
1436         s_qchange(instance, p);
1437     if(type == 3)
1438         s_jump(instance, p);
1439     if(type == 4)
1440         s_startclip(instance, character, p);
1441     return 0;
1442 }
1443 static int c_put(map_t*args) 
1444 {
1445     c_placement(args, 0);
1446     return 0;
1447 }
1448 static int c_change(map_t*args) 
1449 {
1450     c_placement(args, 1);
1451     return 0;
1452 }
1453 static int c_qchange(map_t*args) 
1454 {
1455     c_placement(args, 2);
1456     return 0;
1457 }
1458 static int c_arcchange(map_t*args) 
1459 {
1460     c_placement(args, 2);
1461     return 0;
1462 }
1463 static int c_jump(map_t*args) 
1464 {
1465     c_placement(args, 3);
1466     return 0;
1467 }
1468 static int c_startclip(map_t*args) 
1469 {
1470     c_placement(args, 4);
1471     return 0;
1472 }
1473 static int c_del(map_t*args) 
1474 {
1475     char*instance = lu(args, "name");
1476     s_delinstance(instance);
1477     return 0;
1478 }
1479 static int c_end(map_t*args) 
1480 {
1481     s_end();
1482     return 0;
1483 }
1484 static int c_sprite(map_t*args) 
1485 {
1486     char* name = lu(args, "name");
1487     s_sprite(name);
1488     return 0;
1489 }
1490 static int c_frame(map_t*args) 
1491 {
1492     char*framestr = lu(args, "n");
1493     int frame;
1494     if(isRelative(framestr)) {
1495         frame = s_getframe();
1496         if(getSign(framestr)<0)
1497             syntaxerror("relative frame expressions must be positive");
1498         frame += parseInt(getOffset(framestr));
1499     }
1500     else {
1501         frame = parseInt(framestr);
1502         if(s_getframe() >= frame
1503                 && !(frame==0 && s_getframe()==frame)) // equality is o.k. for frame 0
1504             syntaxerror("frame expression must be >%d (is:%s)", s_getframe(), framestr);
1505     }
1506     s_frame(frame);
1507     return 0;
1508 }
1509 static int c_primitive(map_t*args) 
1510 {
1511     char*name = lu(args, "name");
1512     char*command = lu(args, "commandname");
1513     int width=0, height=0, r=0;
1514     int linewidth = parseTwip(lu(args, "line"));
1515     char*colorstr = lu(args, "color");
1516     RGBA color = parseColor(colorstr);
1517     char*fillstr = lu(args, "fill");
1518     int dofill = 1;
1519     int type=0;
1520     char* font;
1521     char* text;
1522     RGBA fill;
1523     if(!strcmp(command, "circle"))
1524         type = 1;
1525     else if(!strcmp(command, "textshape"))
1526         type = 2;
1527    
1528     if(type==0) {
1529         width = parseTwip(lu(args, "width"));
1530         height = parseTwip(lu(args, "height"));
1531     } else if (type==1) {
1532         r = parseTwip(lu(args, "r"));
1533     } else if (type==2) {
1534         text = lu(args, "text");
1535         font = lu(args, "font");
1536     }
1537
1538     if(!strcmp(fillstr, "fill"))
1539         fillstr = colorstr;
1540     if(!strcmp(fillstr, "none"))
1541         dofill = 0;
1542     if(width<0 || height<0 || linewidth<0 || r<0)
1543         syntaxerror("values width, height, line, r must be positive");
1544     if(!dofill || isColor(fillstr)) {
1545         if(dofill) 
1546             fill = parseColor(fillstr);
1547     } else {
1548         /* FIXME - texture fill */
1549         fill.r = fill.g = 0;
1550         fill.b = fill.a = 255;
1551         warning("texture fill not supported yet. Filling with black.");
1552     }
1553     if(type == 0) s_box(name, width, height, color, linewidth, fill, dofill);
1554     else if(type==1) s_circle(name, r, color, linewidth, fill, dofill);
1555     else if(type==2) s_textshape(name, font, text, color, linewidth, fill, dofill);
1556     return 0;
1557 }
1558
1559 static int c_shape(map_t*args) 
1560 {
1561     char*name = lu(args, "name");
1562     char*filename = lu(args, "filename");
1563     s_shape(name, filename);
1564     return 0;
1565 }
1566
1567 static int c_font(map_t*args) 
1568 {
1569     char*name = lu(args, "name");
1570     char*filename = lu(args, "filename");
1571     s_font(name, filename);
1572     return 0;
1573 }
1574
1575 static int c_text(map_t*args) 
1576 {
1577     char*name = lu(args, "name");
1578     char*text = lu(args, "text");
1579     char*font = lu(args, "font");
1580     float size = parsePercent(lu(args, "size"));
1581     RGBA color = parseColor(lu(args, "color"));
1582     s_text(name, font, text, (int)(size*100), color);
1583     return 0;
1584 }
1585
1586 int fakechar(map_t*args)
1587 {
1588     char*name = lu(args, "name");
1589     s_box(name, 0, 0, black, 20, black, 0);
1590     return 0;
1591 }
1592 static int c_circle(map_t*args) {return fakechar(args);}
1593
1594 static int c_egon(map_t*args) {return fakechar(args);}
1595 static int c_button(map_t*args) {return fakechar(args);}
1596 static int c_edittext(map_t*args) {return fakechar(args);}
1597
1598 static int c_morphshape(map_t*args) {return fakechar(args);}
1599 static int c_image(map_t*args) {return fakechar(args);}
1600 static int c_movie(map_t*args) {return fakechar(args);}
1601 static int c_sound(map_t*args) {return fakechar(args);}
1602
1603 static int c_play(map_t*args) {return 0;}
1604 static int c_stop(map_t*args) {return 0;}
1605
1606 static int c_soundtrack(map_t*args) {return 0;}
1607 static int c_buttonsounds(map_t*args) {return 0;}
1608 static int c_buttonput(map_t*args) {return 0;}
1609 static int c_texture(map_t*args) {return 0;}
1610 static int c_action(map_t*args) {return 0;}
1611
1612 static struct {
1613     char*command;
1614     command_func_t* func;
1615     char*arguments;
1616 } arguments[] =
1617 {{"swf", c_swf, "bbox=autocrop version=5 fps=50 name=!default! @compress=default"},
1618  {"frame", c_frame, "n=<plus>1"},
1619
1620     // "import" type stuff
1621  {"shape", c_shape, "name filename"},
1622  {"morphshape", c_morphshape, "name start end"},
1623  {"jpeg", c_image, "name filename quality=80%"},
1624  {"png", c_image, "name filename"},
1625  {"movie", c_movie, "name filename"},
1626  {"sound", c_sound, "name filename"},
1627  {"font", c_font, "name filename"},
1628  {"soundtrack", c_soundtrack, "filename"},
1629
1630     // character generators
1631  {"box", c_primitive, "name width height color=white line=1 @fill=none"},
1632  {"circle", c_primitive, "name r color=white line=1 @fill=none"},
1633  {"textshape", c_primitive, "name text font color=white line=1 @fill=none"},
1634  {"egon", c_egon, "name vertices color=white line=1 @fill=none"},
1635  {"button", c_button, "name shape over=*shape press=*shape area=*shape"},
1636  {"text", c_text, "name text font size=100% color=white"},
1637  {"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"},
1638  
1639  {"buttonsounds", c_buttonsounds, "name press=0 release=0 enter=0 leave=0"},
1640
1641     // control tags
1642  {"play", c_play, "sound loop=0 @nomultiple=0"},
1643  {"stop", c_stop, "sound"},
1644
1645     // object placement tags
1646  {"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="},
1647  {"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="},
1648  {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1649  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1650  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1651  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= pivot= pin= shear= rotate= above= below="},
1652  {"del", c_del, "name"},
1653     // virtual object placement
1654  {"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="},
1655  {"texture", c_texture, "<i> x=0 y=0 scale= scalex=100% scaley=100% shear=0 rotate=0"},
1656  {"point", c_point, "name x=0 y=0"},
1657
1658     // commands which start a block
1659 //startclip (see above)
1660  {"sprite", c_sprite, "name"},
1661  {"action", c_action, ""},
1662
1663  {"end", c_end, ""}
1664 };
1665
1666
1667 static map_t parseArguments(char*command, char*pattern)
1668 {
1669     char*x;
1670     char*d,*e;
1671    
1672     string_t name[64];
1673     string_t value[64];
1674     int set[64];
1675     int isboolean[64];
1676     int pos;
1677     int len;
1678     int t;
1679     string_t t1,t2;
1680     map_t result;
1681     map_init(&result);
1682
1683     string_set(&t1, "commandname");
1684     string_set(&t2, command);
1685     map_put(&result, t1, t2);
1686
1687     if(!pattern || !*pattern)
1688         return result;
1689
1690     x = pattern;
1691
1692     pos = 0;
1693
1694     if(!strncmp("<i> ", x, 3)) {
1695         readToken();
1696         if(type == COMMAND || type == LABEL) {
1697             pushBack();
1698             syntaxerror("character name expected");
1699         }
1700         name[pos].str = "instance";
1701         name[pos].len = 8;
1702         value[pos].str = text;
1703         value[pos].len = strlen(text);
1704         set[pos] = 1;
1705         pos++;
1706
1707         if(type == ASSIGNMENT)
1708             readToken();
1709
1710         name[pos].str = "character";
1711         name[pos].len = 9;
1712         value[pos].str = text;
1713         value[pos].len = strlen(text);
1714         set[pos] = 1;
1715         pos++;
1716
1717         x+=4;
1718     }
1719
1720     while(*x) {
1721         isboolean[pos] = (x[0] =='@');
1722         if(isboolean[pos])
1723             x++;
1724
1725         d = strchr(x, ' ');
1726         e = strchr(x, '=');
1727         if(!d)
1728             d=&x[strlen(x)];
1729         set[pos] = 0;
1730
1731         if(!e || d<e) { 
1732             // no default
1733             name[pos].str = x;
1734             name[pos].len = d-x;
1735             value[pos].str = 0;
1736             value[pos].len = 0;
1737         } else {
1738             name[pos].str = x;
1739             name[pos].len = e-x;
1740             value[pos].str = e+1;
1741             value[pos].len = d-e-1;
1742         }
1743         pos++;
1744         if(!*d) break;
1745         x=d+1;
1746     }
1747     len = pos;
1748
1749 /*    for(t=0;t<len;t++) {
1750         printf("(%d) %s=%s %s\n", t, strndup(name[t], namelen[t]), strndup(value[t], valuelen[t]),
1751                 isboolean[t]?"(boolean)":"");
1752     }*/
1753
1754     while(1) {
1755         readToken();
1756         if(type == LABEL || type == COMMAND) {
1757             pushBack();
1758             break;
1759         }
1760
1761         // first, search for boolean arguments
1762         for(pos=0;pos<len;pos++)
1763         {
1764             if(!set[pos] && isboolean[pos] && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) {
1765                 set[pos] = 1;
1766                 if(type == ASSIGNMENT)
1767                     readToken();
1768                 value[pos].str = text;
1769                 value[pos].len = strlen(text);
1770                 /*printf("setting boolean parameter %s (to %s)\n",
1771                         strndup(name[pos], namelen[pos]),
1772                         strndup(value[pos], valuelen[pos]));*/
1773                 break;
1774             }
1775         }
1776
1777         // second, search for normal arguments
1778         if(pos==len)
1779         for(pos=0;pos<len;pos++)
1780         {
1781             if((type == ASSIGNMENT && !strncmp(name[pos].str, text, name[pos].len>textlen?name[pos].len:textlen)) ||
1782                (type != ASSIGNMENT && !set[pos])) {
1783                 if(set[pos]) {
1784                     syntaxerror("value %s set twice (old value:%s)", text, strndup(value[pos].str, value[pos].len));
1785                 }
1786                 if(type == ASSIGNMENT)
1787                     readToken();
1788                 set[pos] = 1;
1789                 value[pos].str = text;
1790                 value[pos].len = strlen(text);
1791 #if 0//def DEBUG
1792                 printf("setting parameter %s (to %s)\n",
1793                         strndup(name[pos].str, name[pos].len),
1794                         strndup(value[pos].str, value[pos].len));
1795 #endif
1796                 break;
1797             }
1798         }
1799         if(pos==len) {
1800             syntaxerror("don't know what to do with \"%s\". (All parameters for .%s already set)", text, command);
1801         }
1802     }
1803 #if 0//def DEBUG
1804     for(t=0;t<len;t++) {
1805         printf("%s=%s\n", strndup(name[t].str, name[t].len), strndup(value[t].str, value[t].len));
1806     }
1807 #endif
1808     for(t=0;t<len;t++) {
1809         if(value[t].str && value[t].str[0] == '*') {
1810             //relative default- take value from some other parameter
1811             int s;
1812             for(s=0;s<len;s++) {
1813                 if(value[s].len == value[t].len-1 &&
1814                    !strncmp(&value[t].str[1], value[s].str, value[s].len))
1815                     value[t].str = value[s].str;
1816             }
1817         }
1818         if(value[t].str == 0) {
1819             pushBack();
1820             syntaxerror("value for parameter %s missing (no default)", strndup(name[t].str, name[t].len));
1821         }
1822     }
1823
1824     /* ok, now construct the dictionary from the parameters */
1825
1826     for(t=0;t<len;t++) 
1827     {
1828         map_put(&result, name[t], value[t]);
1829     }
1830     return result;
1831 }
1832 static void parseArgumentsForCommand(char*command)
1833 {
1834     int t;
1835     map_t args;
1836     int nr = -1;
1837     for(t=0;t<sizeof(arguments)/sizeof(arguments[0]);t++) {
1838         if(!strcmp(arguments[t].command, command)) {
1839             args = parseArguments(command, arguments[t].arguments);
1840             nr = t;
1841         }
1842     }
1843     if(nr<0)
1844         syntaxerror("command %s not known", command);
1845
1846 #ifdef DEBUG
1847     printf(".%s\n", command);fflush(stdout);
1848     map_dump(&args, stdout, "\t");fflush(stdout);
1849 #endif
1850
1851     (*arguments[nr].func)(&args);
1852
1853     if(!strcmp(command, "button") ||
1854        !strcmp(command, "action")) {
1855         while(1) {
1856             readToken();
1857             if(type == COMMAND) {
1858                 if(!strcmp(text, "end"))
1859                     break;
1860                 else {
1861                     pushBack();
1862                     break;
1863                 }
1864             }
1865         }
1866     }
1867
1868     map_clear(&args);
1869     return;
1870 }
1871
1872 int main (int argc,char ** argv)
1873
1874     int t;
1875     processargs(argc, argv);
1876     initLog(0,-1,0,0,-1,verbose);
1877
1878     if(!filename) {
1879         args_callback_usage(argv[0]);
1880         exit(1);
1881     }
1882     file = generateTokens(filename);
1883     if(!file) {
1884         printf("parser returned error.\n");
1885         return 1;
1886     }
1887     pos=0;
1888
1889     t=0;
1890
1891     while(!noMoreTokens()) {
1892         readToken();
1893         if(type != COMMAND)
1894             syntaxerror("command expected");
1895         parseArgumentsForCommand(text);
1896     }
1897
1898     s_close();
1899     freeTokens(file);
1900
1901     return 0;
1902 }
1903